From 30b8015debc6de793cf7d8c38f039d3c3834bcc0 Mon Sep 17 00:00:00 2001 From: sjspielman <4701111+sjspielman@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:26:54 +0000 Subject: [PATCH 1/2] Live and rendered notebooks --- RNA-seq/02-gastric_cancer_tximeta-live.Rmd | 16 +- RNA-seq/02-gastric_cancer_tximeta.nb.html | 76 +- .../03-gastric_cancer_exploratory-live.Rmd | 20 +- RNA-seq/03-gastric_cancer_exploratory.nb.html | 86 +- RNA-seq/04-nb_cell_line_tximeta.nb.html | 4 +- RNA-seq/05-nb_cell_line_DESeq2-live.Rmd | 54 +- RNA-seq/05-nb_cell_line_DESeq2.nb.html | 99 +- RNA-seq/06-openpbta_heatmap-live.Rmd | 6 +- RNA-seq/06-openpbta_heatmap.nb.html | 339 +- .../01-intro_to_base_R-live.Rmd | 2 +- .../01-intro_to_base_R.nb.html | 14 +- .../02-intro_to_ggplot2-live.Rmd | 2 +- .../02-intro_to_ggplot2.nb.html | 14 +- .../03-intro_to_tidyverse-live.Rmd | 4 +- .../03-intro_to_tidyverse.nb.html | 16 +- .../01-overrepresentation_analysis-live.Rmd | 62 +- .../01-overrepresentation_analysis.nb.html | 3459 +++++++++++++-- .../02-gene_set_enrichment_analysis-live.Rmd | 37 +- .../02-gene_set_enrichment_analysis.nb.html | 199 +- .../03-gene_set_variation_analysis-live.Rmd | 104 +- .../03-gene_set_variation_analysis.nb.html | 3760 +++++++++++++---- .../01-read_filter_normalize_scRNA.nb.html | 14 +- .../02-dataset_integration-live.Rmd | 2 +- .../02-dataset_integration.nb.html | 22 +- .../03-differential_expression.nb.html | 14 +- .../04-overrepresentation_analysis.nb.html | 14 +- .../05-gene_set_enrichment_analysis.nb.html | 18 +- scRNA-seq/01-scRNA_quant_qc.nb.html | 6 +- scRNA-seq/02-filtering_scRNA.nb.html | 31 +- scRNA-seq/03-normalizing_scRNA.nb.html | 12 +- .../04-dimension_reduction_scRNA.nb.html | 12 +- .../05-clustering_markers_scRNA-live.Rmd | 2 +- scRNA-seq/05-clustering_markers_scRNA.nb.html | 16 +- scRNA-seq/06-celltype_annotation.nb.html | 10 +- 34 files changed, 6835 insertions(+), 1711 deletions(-) diff --git a/RNA-seq/02-gastric_cancer_tximeta-live.Rmd b/RNA-seq/02-gastric_cancer_tximeta-live.Rmd index 645c251d..e9a8bec1 100644 --- a/RNA-seq/02-gastric_cancer_tximeta-live.Rmd +++ b/RNA-seq/02-gastric_cancer_tximeta-live.Rmd @@ -53,8 +53,12 @@ We'll need the `quant.sf` files for all the samples in an experiment which we ha ```{r input-names} # the quant files themselves -sf_files <- list.files(quant_dir, recursive = TRUE, full.names = TRUE, - pattern = "quant.sf") +sf_files <- list.files( + quant_dir, + recursive = TRUE, + full.names = TRUE, + pattern = "quant.sf" +) ``` ```{r metadata-file} @@ -65,7 +69,7 @@ meta_file <- file.path(data_dir, "gastric-cancer_metadata.tsv") **Output** ```{r output-names, live = TRUE} -# Name the output gastric-cancer_tximeta.RDS and use the directory created +# Name the output gastric-cancer_tximeta.rds and use the directory created # above as the rest of the path ``` @@ -98,8 +102,10 @@ sample_names - a `names` column with the sample names ```{r names_sf_files} -coldata <- data.frame(files = sf_files, - names = sample_names) +coldata <- data.frame( + files = sf_files, + names = sample_names +) ``` We have more information about these samples stored in the metadata file that we will also want stored in `coldata`. diff --git a/RNA-seq/02-gastric_cancer_tximeta.nb.html b/RNA-seq/02-gastric_cancer_tximeta.nb.html index 381101a7..5ad82e07 100644 --- a/RNA-seq/02-gastric_cancer_tximeta.nb.html +++ b/RNA-seq/02-gastric_cancer_tximeta.nb.html @@ -3040,7 +3040,7 @@

Libraries and functions

Directories and files

- +
# directory where the data are located
 data_dir <- file.path("data", "gastric-cancer")
 
@@ -3050,9 +3050,7 @@ 

Directories and files

# create a directory to hold the tximeta results if it doesn't exist yet txi_dir <- file.path(data_dir, "txi") -if (!dir.exists(txi_dir)) { - dir.create(txi_dir, recursive = TRUE) -}
+fs::dir_create(txi_dir) @@ -3060,10 +3058,14 @@

Directories and files

experiment which we have stored in quant_dir.

- +
# the quant files themselves
-sf_files <- list.files(quant_dir, recursive = TRUE, full.names = TRUE,
-                       pattern = "quant.sf")
+sf_files <- list.files( + quant_dir, + recursive = TRUE, + full.names = TRUE, + pattern = "quant.sf" +) @@ -3078,10 +3080,10 @@

Directories and files

Output

- -
# Name the output gastric-cancer_tximeta.RDS and use the directory created
+
+
# Name the output gastric-cancer_tximeta.rds and use the directory created
 # above as the rest of the path
-txi_out_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS")
+txi_out_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds")
@@ -3158,9 +3160,11 @@

Set up metadata

quant.sf files - a names column with the sample names

- -
coldata <- data.frame(files = sf_files,
-                      names = sample_names)
+ +
coldata <- data.frame(
+  files = sf_files,
+  names = sample_names
+)
@@ -3260,8 +3264,8 @@

Summarize to gene

# Summarize to the gene level
 gene_summarized <- summarizeToGene(txi_data)
- -
loading existing EnsDb created: 2024-05-29 19:53:16
+ +
loading existing EnsDb created: 2024-08-08 16:10:27
obtaining transcript-to-gene mapping from database
@@ -3436,14 +3440,14 @@

Summarize to gene

# Let's look at the first few rows of the gene-level TPM
 head(assay(gene_summarized, "abundance"))
- +
                SRR585570 SRR585571  SRR585572 SRR585573 SRR585574 SRR585575
-ENSG00000000003 25.016289 18.896831  12.288181 26.911045 22.088410 17.168736
-ENSG00000000005  0.121647  0.000000   0.000000  0.000000  0.000000  0.000000
-ENSG00000000419 26.671423 20.771196 103.246348 69.495297 66.335181 77.471536
-ENSG00000000457  5.657513  2.921236   6.511128  5.107480  5.106009  3.845323
-ENSG00000000460  1.757068  2.933740   1.354462  2.195826  6.341131 16.792151
-ENSG00000000938  1.692824  2.807437   0.078625  0.000000  0.028502  0.000000
+ENSG00000000003 25.014898 18.896831  12.288181 26.911045 22.088410 17.168736
+ENSG00000000005  0.121627  0.000000   0.000000  0.000000  0.000000  0.000000
+ENSG00000000419 26.667591 20.771196 103.246348 69.495297 66.335181 77.471536
+ENSG00000000457  5.653614  2.921236   6.511128  5.107480  5.106009  3.845323
+ENSG00000000460  1.757371  2.933740   1.354462  2.195826  6.341131 16.792151
+ENSG00000000938  1.693652  2.807437   0.078625  0.000000  0.028502  0.000000
                 SRR585576 SRR585577
 ENSG00000000003 17.974009 29.513266
 ENSG00000000005  0.000000  0.000000
@@ -3481,8 +3485,8 @@ 

Session Info

sessionInfo()
- -
R version 4.4.0 (2024-04-24)
+
+
R version 4.4.1 (2024-06-14)
 Platform: x86_64-pc-linux-gnu
 Running under: Ubuntu 22.04.4 LTS
 
@@ -3518,7 +3522,7 @@ 

Session Info

loaded via a namespace (and not attached): [1] DBI_1.2.2 bitops_1.0-7 httr2_1.0.1 [4] biomaRt_2.60.0 rlang_1.1.3 magrittr_2.0.3 - [7] compiler_4.4.0 RSQLite_2.3.6 png_0.1-8 + [7] compiler_4.4.1 RSQLite_2.3.6 png_0.1-8 [10] vctrs_0.6.5 txdbmaker_1.0.0 stringr_1.5.1 [13] ProtGenerics_1.36.0 pkgconfig_2.0.3 crayon_1.5.2 [16] fastmap_1.1.1 dbplyr_2.5.0 XVector_0.44.0 @@ -3527,7 +3531,7 @@

Session Info

[25] bit_4.0.5 xfun_0.43 zlibbioc_1.50.0 [28] cachem_1.0.8 jsonlite_1.8.8 progress_1.2.3 [31] blob_1.2.4 DelayedArray_0.30.0 BiocParallel_1.38.0 -[34] parallel_4.4.0 prettyunits_1.2.0 R6_2.5.1 +[34] parallel_4.4.1 prettyunits_1.2.0 R6_2.5.1 [37] bslib_0.7.0 stringi_1.8.3 rtracklayer_1.64.0 [40] jquerylib_0.1.4 knitr_1.46 readr_2.1.5 [43] Matrix_1.7-0 tidyselect_1.2.1 abind_1.4-5 @@ -3538,20 +3542,20 @@

Session Info

[58] pillar_1.9.0 BiocManager_1.30.22 filelock_1.0.3 [61] generics_0.1.3 vroom_1.6.5 RCurl_1.98-1.14 [64] BiocVersion_3.19.1 hms_1.1.3 glue_1.7.0 -[67] lazyeval_0.2.2 tools_4.4.0 AnnotationHub_3.12.0 -[70] BiocIO_1.14.0 GenomicAlignments_1.40.0 XML_3.99-0.16.1 -[73] grid_4.4.0 GenomeInfoDbData_1.2.12 restfulr_0.0.15 -[76] cli_3.6.2 rappdirs_0.3.3 fansi_1.0.6 -[79] S4Arrays_1.4.0 dplyr_1.1.4 sass_0.4.9 -[82] digest_0.6.35 SparseArray_1.4.0 tximport_1.32.0 -[85] rjson_0.2.21 memoise_2.0.1 htmltools_0.5.8.1 -[88] lifecycle_1.0.4 httr_1.4.7 mime_0.12 -[91] bit64_4.0.5
+[67] lazyeval_0.2.2 tools_4.4.1 AnnotationHub_3.12.0 +[70] BiocIO_1.14.0 GenomicAlignments_1.40.0 fs_1.6.4 +[73] XML_3.99-0.16.1 grid_4.4.1 GenomeInfoDbData_1.2.12 +[76] restfulr_0.0.15 cli_3.6.2 rappdirs_0.3.3 +[79] fansi_1.0.6 S4Arrays_1.4.0 dplyr_1.1.4 +[82] sass_0.4.9 digest_0.6.35 SparseArray_1.4.0 +[85] tximport_1.32.0 rjson_0.2.21 memoise_2.0.1 +[88] htmltools_0.5.8.1 lifecycle_1.0.4 httr_1.4.7 +[91] mime_0.12 bit64_4.0.5
-
LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGdlbmUtbGV2ZWwgc3VtbWFyaXphdGlvbiB3aXRoIGB0eGltZXRhYCIKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIEltcG9ydCBSTkEtc2VxIGV4cHJlc3Npb24gcXVhbnRpZmljYXRpb24gb3V0cHV0IHVzaW5nIGB0eGltZXRhYAotIFN1bW1hcml6ZSB0cmFuc2NyaXB0LWxldmVsIGV4cHJlc3Npb24gdG8gdGhlIGdlbmUgbGV2ZWwKLSBJbnRlcnJvZ2F0ZSBhbmQgZXh0cmFjdCBkYXRhIGZyb20gYSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdAoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBpbXBvcnQgdGhlIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiBxdWFudGlmaWNhdGlvbiBvdXRwdXQgZnJvbSBgc2FsbW9uIHF1YW50YCB1c2luZyB0aGUgW2B0eGltZXRhYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3R4aW1ldGEuaHRtbCkgcGFja2FnZS4KYHR4aW1ldGFgIGlzIGluIHBhcnQgYSB3cmFwcGVyIGFyb3VuZCBhbm90aGVyIHBhY2thZ2UsIFtgdHhpbXBvcnRgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvdHhpbXBvcnQuaHRtbCksIHdoaWNoIGltcG9ydHMgdHJhbnNjcmlwdCBleHByZXNzaW9uIGRhdGEgYW5kIHN1bW1hcml6ZXMgaXQgdG8gdGhlIGdlbmUgbGV2ZWwuCldvcmtpbmcgYXQgdGhlIGdlbmUgcmF0aGVyIHRoYW4gdHJhbnNjcmlwdCBsZXZlbCBoYXMgYSBudW1iZXIgb2YgcG90ZW50aWFsIGFkdmFudGFnZXMgZm9yIGludGVycHJldGFiaWxpdHksIGVmZmljaWVuY3ksIGFuZCByZWR1Y3Rpb24gb2YgZmFsc2UgcG9zaXRpdmVzIChbU29uZXNvbiBfZXQgYWwuXyAyMDE2XShodHRwczovL2RvaS5vcmcvMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43NTYzLjIpKS4KYHR4aW1ldGFgIGVhc2VzIHNvbWUgb2YgdGhlIGJ1cmRlbiBvZiBpbXBvcnQgYnkgYXV0b21hdGljYWxseSBpZGVudGlmeWluZyB0aGUgY29ycmVjdCBzZXQgb2YgYW5ub3RhdGlvbiBkYXRhIHRvIGFwcGVuZCB0byBtYW55IGRhdGEgc2V0cyAoW0xvdmUgX2V0IGFsLl8gMjAyMF0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDA3NjY0KSkuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV81LnBuZykKCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGB0eGltZXRhYCwgc2VlIFt0aGlzIGV4Y2VsbGVudCB2aWduZXR0ZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCkgZnJvbSBMb3ZlIF9ldCBhbF8uCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeX0KIyBMb2FkIHRoZSB0eGltZXRhIHBhY2thZ2UKbGlicmFyeSh0eGltZXRhKQoKIyBMb2FkIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBwYWNrYWdlCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmBgYAoKIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpgYGB7ciBkaXJlY3RvcmllcywgbGl2ZSA9IFRSVUV9CiMgZGlyZWN0b3J5IHdoZXJlIHRoZSBkYXRhIGFyZSBsb2NhdGVkCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJnYXN0cmljLWNhbmNlciIpCgojIGRpcmVjdG9yeSB3aGVyZSB0aGUgcXVhbnQgZmlsZXMgYXJlIGxvY2F0ZWQsIGVhY2ggc2FtcGxlIGlzIGl0cyBvd24KIyBkaXJlY3RvcnkKcXVhbnRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInNhbG1vbl9xdWFudCIpCgojIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSB0eGltZXRhIHJlc3VsdHMgaWYgaXQgZG9lc24ndCBleGlzdCB5ZXQKdHhpX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJ0eGkiKQppZiAoIWRpci5leGlzdHModHhpX2RpcikpIHsKICBkaXIuY3JlYXRlKHR4aV9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgpXZSdsbCBuZWVkIHRoZSBgcXVhbnQuc2ZgIGZpbGVzIGZvciBhbGwgdGhlIHNhbXBsZXMgaW4gYW4gZXhwZXJpbWVudCB3aGljaCB3ZSBoYXZlIHN0b3JlZCBpbiBgcXVhbnRfZGlyYC4KCmBgYHtyIGlucHV0LW5hbWVzfQojIHRoZSBxdWFudCBmaWxlcyB0aGVtc2VsdmVzCnNmX2ZpbGVzIDwtIGxpc3QuZmlsZXMocXVhbnRfZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBmdWxsLm5hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gInF1YW50LnNmIikKYGBgCgpgYGB7ciBtZXRhZGF0YS1maWxlfQojIHNhbXBsZSBtZXRhZGF0YSBmaWxlCm1ldGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJnYXN0cmljLWNhbmNlcl9tZXRhZGF0YS50c3YiKQpgYGAKCioqT3V0cHV0KioKCmBgYHtyIG91dHB1dC1uYW1lcywgbGl2ZSA9IFRSVUV9CiMgTmFtZSB0aGUgb3V0cHV0IGdhc3RyaWMtY2FuY2VyX3R4aW1ldGEuUkRTIGFuZCB1c2UgdGhlIGRpcmVjdG9yeSBjcmVhdGVkCiMgYWJvdmUgYXMgdGhlIHJlc3Qgb2YgdGhlIHBhdGgKdHhpX291dF9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5SRFMiKQpgYGAKCiMjIEZpbGUgbmFtZXMKCkFsbCBvdXRwdXQgZmlsZXMgZnJvbSBgc2FsbW9uIHF1YW50YCB3ZSdsbCB1c2Ugd2l0aCBgdHhpbWV0YWAgYXJlIG5hbWVkIGBxdWFudC5zZmAuClVuZm9ydHVuYXRlbHksIHRoaXMgbWVhbnMgdGhhdCB0aGUgZmlsZSBuYW1lcyB0aGVtc2VsdmVzIGRvIG5vdCBoYXZlIGFueSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlIHRoZXkgY29tZSBmcm9tIQoKYGBge3Igc2ZfZmlsZXMsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZ1bGwgcGF0aCBmb3IgdGhlIHF1YW50LnNmIGZpbGVzCnNmX2ZpbGVzCmBgYAoKTGV0J3MgZXh0cmFjdCB0aGUgX3NhbXBsZV8gbmFtZXMgZnJvbSB0aGUgKipmaWxlIHBhdGhzKiogdXNpbmcgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLgoKTm90aWNlIGhvdyB0aGUgZmlsZSBwYXRoIGlzIHNlcGFyYXRlZCBieSBgL2AuCklmIHdlIHdlcmUgdG8gc3BsaXQgdXAgdGhpcyBjaGFyYWN0ZXIgc3RyaW5nIGJ5IGAvYCwgdGhlIHNlY29uZCB0byBsYXN0IGl0ZW0gaXMgdGhlIHNhbXBsZSBuYW1lcyAoYmVjYXVzZSB3ZSB1c2VkIHRoZW0gYXMgZGlyZWN0b3J5IG5hbWVzIGZvciB0aGUgYHNhbG1vbmAgb3V0cHV0KS4KVGhpcyBpcyBleGFjdGx5IHdoYXQgYHN0cmluZ3I6OndvcmQoKWAgYWxsb3dzIHVzIHRvIGRvOiBzcGxpdCB1cCB0aGUgZmlsZSBwYXRocyBieSBgL2AgYW5kIGV4dHJhY3QgdGhlIHNhbXBsZSBuYW1lcy4KCmBgYHtyIHNhbXBsZV9uYW1lc30Kc2FtcGxlX25hbWVzIDwtIHN0cmluZ3I6OndvcmQoc2ZfZmlsZXMsIC0yLCBzZXAgPSAiLyIpCnNhbXBsZV9uYW1lcwpgYGAKCiMjIFNldCB1cCBtZXRhZGF0YQoKYHR4aW1ldGFgIG5lZWRzIGEgZGF0YSBmcmFtZSB3aXRoIGF0IGxlYXN0IHRoZXNlIHR3byBjb2x1bW5zOgotIGEgYGZpbGVzYCBjb2x1bW4gIHdpdGggdGhlIGZpbGUgcGF0aHMgdG8gdGhlIHF1YW50LnNmIGZpbGVzCi0gYSBgbmFtZXNgIGNvbHVtbiB3aXRoIHRoZSBzYW1wbGUgbmFtZXMKCmBgYHtyIG5hbWVzX3NmX2ZpbGVzfQpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoZmlsZXMgPSBzZl9maWxlcywKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzID0gc2FtcGxlX25hbWVzKQpgYGAKCldlIGhhdmUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGVzZSBzYW1wbGVzIHN0b3JlZCBpbiB0aGUgbWV0YWRhdGEgZmlsZSB0aGF0IHdlIHdpbGwgYWxzbyB3YW50IHN0b3JlZCBpbiBgY29sZGF0YWAuCkxldCdzIHJlYWQgaW4gdGhlIHNhbXBsZSBtZXRhZGF0YSBmcm9tIHRoZSBUU1YgZmlsZS4KCmBgYHtyIHNhbXBsZV9tZXRhX2RmLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBzYW1wbGUgbWV0YWRhdGEgVFNWIGZpbGUgYW5kIGhhdmUgYSBsb29rCnNhbXBsZV9tZXRhX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihtZXRhX2ZpbGUpCnNhbXBsZV9tZXRhX2RmCmBgYAoKV2UnbGwgd2FudCB0aGlzIGluZm9ybWF0aW9uIHRvIGJlIGFkZGVkIHRvIHRoZSBgY29sZGF0YWAsIHdoaWNoIHdlIGNhbiBkbyBieSB1c2luZyBhIGpvaW4gZnVuY3Rpb24gdG8gbWF0Y2ggdXAgdGhlIHJvd3MgYmV0d2VlbiB0aGUgdHdvIGRhdGEgZnJhbWVzIGFuZCBjb21iaW5lIHRoZW0uCgpgYGB7ciBqb2luLXNhbXBsZV9tZXRhX2RmfQpjb2xkYXRhIDwtIGNvbGRhdGEgfD4KICBkcGx5cjo6aW5uZXJfam9pbihzYW1wbGVfbWV0YV9kZiwgYnkgPSBjKCJuYW1lcyIgPSAic3JyX2FjY2Vzc2lvbiIpKQoKY29sZGF0YQpgYGAKCiMjIEltcG9ydCBleHByZXNzaW9uIGRhdGEgd2l0aCBgdHhpbWV0YWAKClVzaW5nIHRoZSBgY29sZGF0YWAgZGF0YSBmcmFtZSB0aGF0IHdlIHNldCB1cCwgd2UgY2FuIG5vdyBydW4gdGhlIGB0eGltZXRhKClgIHRvIGltcG9ydCBvdXIgZXhwcmVzc2lvbiBkYXRhIHdoaWxlIGF1dG9tYXRpY2FsbHkgZmluZGluZyBhbmQgYXNzb2NpYXRpbmcgdGhlIHRyYW5zY3JpcHQgYW5ub3RhdGlvbnMgdGhhdCB3ZXJlIHVzZWQgd2hlbiB3ZSBwZXJmb3JtZWQgdGhlIHF1YW50aWZpY2F0aW9uLgoKVGhlIGZpcnN0IHRpbWUgeW91IHJ1biBgdHhpbWV0YSgpYCB5b3UgbWF5IGdldCBhIG1lc3NhZ2UgYWJvdXQgc3RvcmluZyBkb3dubG9hZGVkIHRyYW5zY3JpcHRvbWUgZGF0YSBpbiBhIGNhY2hlIGRpcmVjdG9yeSBzbyB0aGF0IGl0IGNhbiByZXRyaWV2ZSB0aGUgZGF0YSBtb3JlIHF1aWNrbHkgdGhlIG5leHQgdGltZS4KV2UgcmVjb21tZW5kIHlvdSB1c2UgdGhlIGNhY2hlLCBhbmQgYWNjZXB0IHRoZSBkZWZhdWx0IGxvY2F0aW9uLgoKYGBge3IgdHhpbWV0YSwgbGl2ZSA9IFRSVUV9CnR4aV9kYXRhIDwtIHR4aW1ldGEoY29sZGF0YSkKYGBgCgoqdHhpbWV0YSBjdXJyZW50bHkgd29ya3MgZWFzaWx5IGZvciBtb3N0IGh1bWFuIGFuZCBtb3VzZSBkYXRhc2V0cywgYnV0IHJlcXVpcmVzIGEgW2ZldyBtb3JlIHN0ZXBzIGZvciBvdGhlciBzcGVjaWVzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCNXaGF0X2lmX2NoZWNrc3VtX2lzbiVFMiU4MCU5OXRfa25vd24pLgoKIyMgU3VtbWFyaXplIHRvIGdlbmUKCldlJ2xsIHN1bW1hcml6ZSB0byB0aGUgZ2VuZSBsZXZlbCB1c2luZyB0aGUgYHN1bW1hcml6ZVRvR2VuZSgpYCBmdW5jdGlvbi4KCmBgYHtyIHN1bW1hcml6ZS1nZW5lfQojIFN1bW1hcml6ZSB0byB0aGUgZ2VuZSBsZXZlbApnZW5lX3N1bW1hcml6ZWQgPC0gc3VtbWFyaXplVG9HZW5lKHR4aV9kYXRhKQpgYGAKCldlIGNhbiB1c2UgdGhlIGBjbGFzc2AgZnVuY3Rpb24gdG8gc2VlIHdoYXQgdHlwZSBvZiBvYmplY3QgYGdlbmVfc3VtbWFyaXplZGAgaXMuCgpgYGB7ciBjbGFzcywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0eXBlIG9mIG9iamVjdCBgZ2VuZV9zdW1tYXJpemVkYCBpcwpjbGFzcyhnZW5lX3N1bW1hcml6ZWQpCmBgYAoKVGhpcyB0ZWxscyB1cyB0aGF0IGBnZW5lX3N1bW1hcml6ZWRgIGlzIGFuIG9iamVjdCBjYWxsZWQgYSBbYFN1bW1hcml6ZWRFeHBlcmltZW50YF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL1N1bW1hcml6ZWRFeHBlcmltZW50L3ZlcnNpb25zLzEuMi4zL3RvcGljcy9TdW1tYXJpemVkRXhwZXJpbWVudC1jbGFzcykgd2hpY2ggY2FuIGJlIGhhbmRsZWQgYnkgZnVuY3Rpb25zIGZyb20gdGhlIHBhY2thZ2Ugb2YgdGhlIHNhbWUgbmFtZS4KV2UgbW9yZSBzcGVjaWZpY2FsbHkgaGF2ZSBhIGBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudGAgd2hpY2ggaXMgYSBtb3JlIHNwZWNpZmljIHR5cGUgb2YgYFN1bW1hcml6ZWRFeHBlcmltZW50YC4KCmBTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0cyBoYXZlIHRoaXMgZ2VuZXJhbCBzdHJ1Y3R1cmU6CgohW1N1bW1hcml6ZWRFeHBlcmltZW50XShkaWFncmFtcy9TdW1tYXJpemVFeHBlcmltZW50LXN0cnVjdHVyZS5wbmcpCgpUaGlzIGZpZ3VyZSBpcyBmcm9tIHRoaXMgaGFuZHkgdmlnbmV0dGUgYWJvdXQgW2BTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0c10oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9TdW1tYXJpemVkRXhwZXJpbWVudC9pbnN0L2RvYy9TdW1tYXJpemVkRXhwZXJpbWVudC5odG1sKS4KCkFzIHNob3duIGluIHRoZSBkaWFncmFtLCB3ZSBjYW4gdXNlIHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyBwcm92aWRlZCBieSB0aGUgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBwYWNrYWdlIHRvIGV4dHJhY3QgZGF0YSBmcm9tIG91ciBgZ2VuZV9zdW1tYXJpemVkYCBvYmplY3QuCkZvciBleGFtcGxlLCBjYWxsaW5nIGByb3dEYXRhKClgIG9uIG91ciBvYmplY3Qgc2hvd3MgYWxsIHRoZSBnZW5lIGluZm9ybWF0aW9uIHRoYXQgYHR4aW1ldGFgIHNldCB1cCEKCmBgYHtyfQojIHJvd0RhdGEoKSBzaG93cyB1cyBvdXIgZ2VuZSBhbm5vdGF0aW9uCnJvd0RhdGEoZ2VuZV9zdW1tYXJpemVkKQpgYGAKClRoZSBgYXNzYXlgIHNsb3QgaW4gYFN1bW1hcml6ZWRFeHBlcmltZW50YHMgaG9sZHMgZGF0YSBmcm9tIHRoZSBleHBlcmltZW50LgpJbiB0aGlzIGNhc2UsIGl0IHdpbGwgaW5jbHVkZSBvdXIgZ2VuZS1sZXZlbCBleHByZXNzaW9uIGluZm9ybWF0aW9uIHN0b3JlZCBhcyBhIGdlbmUgeCBzYW1wbGUgbWF0cml4LgoKTXVsdGlwbGUgYGFzc2F5c2AgY2FuIGJlIHN0b3JlZCBpbiBhbiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIGFuZCB3ZSBjYW4gdXNlIHRoZSBgYXNzYXlOYW1lcygpYCBmdW5jdGlvbiB0byBzZWUgd2hhdCBhc3NheXMgYXJlIGluY2x1ZGVkIGluIGBnZW5lX3N1bW1hcml6ZWRgLgoKYGBge3IgYXNzYXktbmFtZXMsIGxpdmUgPSBUUlVFfQphc3NheU5hbWVzKGdlbmVfc3VtbWFyaXplZCkKYGBgCgpJZiB3ZSB3YW50IHRvIGV4dHJhY3QgYW4gYGFzc2F5YCdzIGRhdGEsIHdlIGNhbiB1c2UgYGFzc2F5KClgIGZ1bmN0aW9uIGFuZCBzcGVjaWZ5IHRoZSBuYW1lIG9mIHRoZSBhc3NheSB3ZSB3YW50IHRvIGV4dHJhY3QuCgpgYGB7ciBhc3NheS1jb3VudHMsIGxpdmUgPSBUUlVFfQpjb3VudHNfbWF0IDwtIGFzc2F5KGdlbmVfc3VtbWFyaXplZCwgImNvdW50cyIpCmBgYAoKV2UgY2FuIHVzZSB0aGUgYGNsYXNzYCBmdW5jdGlvbiB0byBzZWUgd2hhdCB0eXBlIG9mIG9iamVjdCB0aGUgYGFzc2F5KClgIGZ1bmN0aW9uIHJldHVybnMuCgpgYGB7ciBjbGFzcy1jb3VudHMsIGxpdmUgPSBUUlVFfQojIENoZWNrIHdoYXQgdHlwZSBvZiBvYmplY3QgYGNvdW50c19tYXRgIGlzCmNsYXNzKGNvdW50c19tYXQpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgZXh0cmFjdCB0aGUgVFBNIGRhdGEgLS0gY2FsbGVkIGBhYnVuZGFuY2VgIGZyb20gYGdlbmVfc3VtbWFyaXplZGAuCgpgYGB7ciBhc3NheS1hYnVuZGFuY2UsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBnZW5lLWxldmVsIFRQTQpoZWFkKGFzc2F5KGdlbmVfc3VtbWFyaXplZCwgImFidW5kYW5jZSIpKQpgYGAKCiMjIFNhdmUgdG8gZmlsZQoKV2UgY291bGQgdXNlIGByZWFkcjo6d3JpdGVfdHN2YCB0byBzYXZlIGBjb3VudHNfbWF0YCBvbmx5IGJ1dCBgZ2VuZV9zdW1tYXJpemVkYCBoYXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gc3RvcmVkIGhlcmUgYmV5b25kIHRoZSBjb3VudHMsIHNvIHdlIG1heSB3YW50IHRvIHNhdmUgYWxsIG9mIHRoaXMgdG8gYSBSRFMgb2JqZWN0LgoKYGBge3Igd3JpdGUtdHhpLCBsaXZlID0gVFJVRX0KIyBXcml0ZSBgZ2VuZV9zdW1tYXJpemVkYCB0byBSRFMgb2JqZWN0CnJlYWRyOjp3cml0ZV9yZHMoZ2VuZV9zdW1tYXJpemVkLCBmaWxlID0gdHhpX291dF9maWxlKQpgYGAKCldlJ2xsIGltcG9ydCB0aGlzIHdpdGggdGhlIGBERVNlcTJgIHBhY2thZ2UgaW4gdGhlIG5leHQgbm90ZWJvb2suCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
+
LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGdlbmUtbGV2ZWwgc3VtbWFyaXphdGlvbiB3aXRoIGB0eGltZXRhYCIKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIEltcG9ydCBSTkEtc2VxIGV4cHJlc3Npb24gcXVhbnRpZmljYXRpb24gb3V0cHV0IHVzaW5nIGB0eGltZXRhYAotIFN1bW1hcml6ZSB0cmFuc2NyaXB0LWxldmVsIGV4cHJlc3Npb24gdG8gdGhlIGdlbmUgbGV2ZWwKLSBJbnRlcnJvZ2F0ZSBhbmQgZXh0cmFjdCBkYXRhIGZyb20gYSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdAoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBpbXBvcnQgdGhlIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiBxdWFudGlmaWNhdGlvbiBvdXRwdXQgZnJvbSBgc2FsbW9uIHF1YW50YCB1c2luZyB0aGUgW2B0eGltZXRhYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3R4aW1ldGEuaHRtbCkgcGFja2FnZS4KYHR4aW1ldGFgIGlzIGluIHBhcnQgYSB3cmFwcGVyIGFyb3VuZCBhbm90aGVyIHBhY2thZ2UsIFtgdHhpbXBvcnRgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvdHhpbXBvcnQuaHRtbCksIHdoaWNoIGltcG9ydHMgdHJhbnNjcmlwdCBleHByZXNzaW9uIGRhdGEgYW5kIHN1bW1hcml6ZXMgaXQgdG8gdGhlIGdlbmUgbGV2ZWwuCldvcmtpbmcgYXQgdGhlIGdlbmUgcmF0aGVyIHRoYW4gdHJhbnNjcmlwdCBsZXZlbCBoYXMgYSBudW1iZXIgb2YgcG90ZW50aWFsIGFkdmFudGFnZXMgZm9yIGludGVycHJldGFiaWxpdHksIGVmZmljaWVuY3ksIGFuZCByZWR1Y3Rpb24gb2YgZmFsc2UgcG9zaXRpdmVzIChbU29uZXNvbiBfZXQgYWwuXyAyMDE2XShodHRwczovL2RvaS5vcmcvMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43NTYzLjIpKS4KYHR4aW1ldGFgIGVhc2VzIHNvbWUgb2YgdGhlIGJ1cmRlbiBvZiBpbXBvcnQgYnkgYXV0b21hdGljYWxseSBpZGVudGlmeWluZyB0aGUgY29ycmVjdCBzZXQgb2YgYW5ub3RhdGlvbiBkYXRhIHRvIGFwcGVuZCB0byBtYW55IGRhdGEgc2V0cyAoW0xvdmUgX2V0IGFsLl8gMjAyMF0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDA3NjY0KSkuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV81LnBuZykKCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGB0eGltZXRhYCwgc2VlIFt0aGlzIGV4Y2VsbGVudCB2aWduZXR0ZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCkgZnJvbSBMb3ZlIF9ldCBhbF8uCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeX0KIyBMb2FkIHRoZSB0eGltZXRhIHBhY2thZ2UKbGlicmFyeSh0eGltZXRhKQoKIyBMb2FkIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBwYWNrYWdlCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmBgYAoKIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpgYGB7ciBkaXJlY3RvcmllcywgbGl2ZSA9IFRSVUV9CiMgZGlyZWN0b3J5IHdoZXJlIHRoZSBkYXRhIGFyZSBsb2NhdGVkCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJnYXN0cmljLWNhbmNlciIpCgojIGRpcmVjdG9yeSB3aGVyZSB0aGUgcXVhbnQgZmlsZXMgYXJlIGxvY2F0ZWQsIGVhY2ggc2FtcGxlIGlzIGl0cyBvd24KIyBkaXJlY3RvcnkKcXVhbnRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInNhbG1vbl9xdWFudCIpCgojIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSB0eGltZXRhIHJlc3VsdHMgaWYgaXQgZG9lc24ndCBleGlzdCB5ZXQKdHhpX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJ0eGkiKQpmczo6ZGlyX2NyZWF0ZSh0eGlfZGlyKQpgYGAKCldlJ2xsIG5lZWQgdGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGFsbCB0aGUgc2FtcGxlcyBpbiBhbiBleHBlcmltZW50IHdoaWNoIHdlIGhhdmUgc3RvcmVkIGluIGBxdWFudF9kaXJgLgoKYGBge3IgaW5wdXQtbmFtZXN9CiMgdGhlIHF1YW50IGZpbGVzIHRoZW1zZWx2ZXMKc2ZfZmlsZXMgPC0gbGlzdC5maWxlcygKICBxdWFudF9kaXIsIAogIHJlY3Vyc2l2ZSA9IFRSVUUsIAogIGZ1bGwubmFtZXMgPSBUUlVFLAogIHBhdHRlcm4gPSAicXVhbnQuc2YiCikKYGBgCgpgYGB7ciBtZXRhZGF0YS1maWxlfQojIHNhbXBsZSBtZXRhZGF0YSBmaWxlCm1ldGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJnYXN0cmljLWNhbmNlcl9tZXRhZGF0YS50c3YiKQpgYGAKCioqT3V0cHV0KioKCmBgYHtyIG91dHB1dC1uYW1lcywgbGl2ZSA9IFRSVUV9CiMgTmFtZSB0aGUgb3V0cHV0IGdhc3RyaWMtY2FuY2VyX3R4aW1ldGEucmRzIGFuZCB1c2UgdGhlIGRpcmVjdG9yeSBjcmVhdGVkCiMgYWJvdmUgYXMgdGhlIHJlc3Qgb2YgdGhlIHBhdGgKdHhpX291dF9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5yZHMiKQpgYGAKCiMjIEZpbGUgbmFtZXMKCkFsbCBvdXRwdXQgZmlsZXMgZnJvbSBgc2FsbW9uIHF1YW50YCB3ZSdsbCB1c2Ugd2l0aCBgdHhpbWV0YWAgYXJlIG5hbWVkIGBxdWFudC5zZmAuClVuZm9ydHVuYXRlbHksIHRoaXMgbWVhbnMgdGhhdCB0aGUgZmlsZSBuYW1lcyB0aGVtc2VsdmVzIGRvIG5vdCBoYXZlIGFueSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlIHRoZXkgY29tZSBmcm9tIQoKYGBge3Igc2ZfZmlsZXMsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZ1bGwgcGF0aCBmb3IgdGhlIHF1YW50LnNmIGZpbGVzCnNmX2ZpbGVzCmBgYAoKTGV0J3MgZXh0cmFjdCB0aGUgX3NhbXBsZV8gbmFtZXMgZnJvbSB0aGUgKipmaWxlIHBhdGhzKiogdXNpbmcgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLgoKTm90aWNlIGhvdyB0aGUgZmlsZSBwYXRoIGlzIHNlcGFyYXRlZCBieSBgL2AuCklmIHdlIHdlcmUgdG8gc3BsaXQgdXAgdGhpcyBjaGFyYWN0ZXIgc3RyaW5nIGJ5IGAvYCwgdGhlIHNlY29uZCB0byBsYXN0IGl0ZW0gaXMgdGhlIHNhbXBsZSBuYW1lcyAoYmVjYXVzZSB3ZSB1c2VkIHRoZW0gYXMgZGlyZWN0b3J5IG5hbWVzIGZvciB0aGUgYHNhbG1vbmAgb3V0cHV0KS4KVGhpcyBpcyBleGFjdGx5IHdoYXQgYHN0cmluZ3I6OndvcmQoKWAgYWxsb3dzIHVzIHRvIGRvOiBzcGxpdCB1cCB0aGUgZmlsZSBwYXRocyBieSBgL2AgYW5kIGV4dHJhY3QgdGhlIHNhbXBsZSBuYW1lcy4KCmBgYHtyIHNhbXBsZV9uYW1lc30Kc2FtcGxlX25hbWVzIDwtIHN0cmluZ3I6OndvcmQoc2ZfZmlsZXMsIC0yLCBzZXAgPSAiLyIpCnNhbXBsZV9uYW1lcwpgYGAKCiMjIFNldCB1cCBtZXRhZGF0YQoKYHR4aW1ldGFgIG5lZWRzIGEgZGF0YSBmcmFtZSB3aXRoIGF0IGxlYXN0IHRoZXNlIHR3byBjb2x1bW5zOgotIGEgYGZpbGVzYCBjb2x1bW4gIHdpdGggdGhlIGZpbGUgcGF0aHMgdG8gdGhlIHF1YW50LnNmIGZpbGVzCi0gYSBgbmFtZXNgIGNvbHVtbiB3aXRoIHRoZSBzYW1wbGUgbmFtZXMKCmBgYHtyIG5hbWVzX3NmX2ZpbGVzfQpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoCiAgZmlsZXMgPSBzZl9maWxlcywKICBuYW1lcyA9IHNhbXBsZV9uYW1lcwopCmBgYAoKV2UgaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIHNhbXBsZXMgc3RvcmVkIGluIHRoZSBtZXRhZGF0YSBmaWxlIHRoYXQgd2Ugd2lsbCBhbHNvIHdhbnQgc3RvcmVkIGluIGBjb2xkYXRhYC4KTGV0J3MgcmVhZCBpbiB0aGUgc2FtcGxlIG1ldGFkYXRhIGZyb20gdGhlIFRTViBmaWxlLgoKYGBge3Igc2FtcGxlX21ldGFfZGYsIGxpdmUgPSBUUlVFfQojIFJlYWQgaW4gdGhlIHNhbXBsZSBtZXRhZGF0YSBUU1YgZmlsZSBhbmQgaGF2ZSBhIGxvb2sKc2FtcGxlX21ldGFfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KG1ldGFfZmlsZSkKc2FtcGxlX21ldGFfZGYKYGBgCgpXZSdsbCB3YW50IHRoaXMgaW5mb3JtYXRpb24gdG8gYmUgYWRkZWQgdG8gdGhlIGBjb2xkYXRhYCwgd2hpY2ggd2UgY2FuIGRvIGJ5IHVzaW5nIGEgam9pbiBmdW5jdGlvbiB0byBtYXRjaCB1cCB0aGUgcm93cyBiZXR3ZWVuIHRoZSB0d28gZGF0YSBmcmFtZXMgYW5kIGNvbWJpbmUgdGhlbS4KCmBgYHtyIGpvaW4tc2FtcGxlX21ldGFfZGZ9CmNvbGRhdGEgPC0gY29sZGF0YSB8PgogIGRwbHlyOjppbm5lcl9qb2luKHNhbXBsZV9tZXRhX2RmLCBieSA9IGMoIm5hbWVzIiA9ICJzcnJfYWNjZXNzaW9uIikpCgpjb2xkYXRhCmBgYAoKIyMgSW1wb3J0IGV4cHJlc3Npb24gZGF0YSB3aXRoIGB0eGltZXRhYAoKVXNpbmcgdGhlIGBjb2xkYXRhYCBkYXRhIGZyYW1lIHRoYXQgd2Ugc2V0IHVwLCB3ZSBjYW4gbm93IHJ1biB0aGUgYHR4aW1ldGEoKWAgdG8gaW1wb3J0IG91ciBleHByZXNzaW9uIGRhdGEgd2hpbGUgYXV0b21hdGljYWxseSBmaW5kaW5nIGFuZCBhc3NvY2lhdGluZyB0aGUgdHJhbnNjcmlwdCBhbm5vdGF0aW9ucyB0aGF0IHdlcmUgdXNlZCB3aGVuIHdlIHBlcmZvcm1lZCB0aGUgcXVhbnRpZmljYXRpb24uCgpUaGUgZmlyc3QgdGltZSB5b3UgcnVuIGB0eGltZXRhKClgIHlvdSBtYXkgZ2V0IGEgbWVzc2FnZSBhYm91dCBzdG9yaW5nIGRvd25sb2FkZWQgdHJhbnNjcmlwdG9tZSBkYXRhIGluIGEgY2FjaGUgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIHJldHJpZXZlIHRoZSBkYXRhIG1vcmUgcXVpY2tseSB0aGUgbmV4dCB0aW1lLgpXZSByZWNvbW1lbmQgeW91IHVzZSB0aGUgY2FjaGUsIGFuZCBhY2NlcHQgdGhlIGRlZmF1bHQgbG9jYXRpb24uCgpgYGB7ciB0eGltZXRhLCBsaXZlID0gVFJVRX0KdHhpX2RhdGEgPC0gdHhpbWV0YShjb2xkYXRhKQpgYGAKCip0eGltZXRhIGN1cnJlbnRseSB3b3JrcyBlYXNpbHkgZm9yIG1vc3QgaHVtYW4gYW5kIG1vdXNlIGRhdGFzZXRzLCBidXQgcmVxdWlyZXMgYSBbZmV3IG1vcmUgc3RlcHMgZm9yIG90aGVyIHNwZWNpZXNdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3R4aW1ldGEvaW5zdC9kb2MvdHhpbWV0YS5odG1sI1doYXRfaWZfY2hlY2tzdW1faXNuJUUyJTgwJTk5dF9rbm93bikuCgojIyBTdW1tYXJpemUgdG8gZ2VuZQoKV2UnbGwgc3VtbWFyaXplIHRvIHRoZSBnZW5lIGxldmVsIHVzaW5nIHRoZSBgc3VtbWFyaXplVG9HZW5lKClgIGZ1bmN0aW9uLgoKYGBge3Igc3VtbWFyaXplLWdlbmV9CiMgU3VtbWFyaXplIHRvIHRoZSBnZW5lIGxldmVsCmdlbmVfc3VtbWFyaXplZCA8LSBzdW1tYXJpemVUb0dlbmUodHhpX2RhdGEpCmBgYAoKV2UgY2FuIHVzZSB0aGUgYGNsYXNzYCBmdW5jdGlvbiB0byBzZWUgd2hhdCB0eXBlIG9mIG9iamVjdCBgZ2VuZV9zdW1tYXJpemVkYCBpcy4KCmBgYHtyIGNsYXNzLCBsaXZlID0gVFJVRX0KIyBDaGVjayB3aGF0IHR5cGUgb2Ygb2JqZWN0IGBnZW5lX3N1bW1hcml6ZWRgIGlzCmNsYXNzKGdlbmVfc3VtbWFyaXplZCkKYGBgCgpUaGlzIHRlbGxzIHVzIHRoYXQgYGdlbmVfc3VtbWFyaXplZGAgaXMgYW4gb2JqZWN0IGNhbGxlZCBhIFtgU3VtbWFyaXplZEV4cGVyaW1lbnRgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvU3VtbWFyaXplZEV4cGVyaW1lbnQvdmVyc2lvbnMvMS4yLjMvdG9waWNzL1N1bW1hcml6ZWRFeHBlcmltZW50LWNsYXNzKSB3aGljaCBjYW4gYmUgaGFuZGxlZCBieSBmdW5jdGlvbnMgZnJvbSB0aGUgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lLgpXZSBtb3JlIHNwZWNpZmljYWxseSBoYXZlIGEgYFJhbmdlZFN1bW1hcml6ZWRFeHBlcmltZW50YCB3aGljaCBpcyBhIG1vcmUgc3BlY2lmaWMgdHlwZSBvZiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgLgoKYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3RzIGhhdmUgdGhpcyBnZW5lcmFsIHN0cnVjdHVyZToKCiFbU3VtbWFyaXplZEV4cGVyaW1lbnRdKGRpYWdyYW1zL1N1bW1hcml6ZUV4cGVyaW1lbnQtc3RydWN0dXJlLnBuZykKClRoaXMgZmlndXJlIGlzIGZyb20gdGhpcyBoYW5keSB2aWduZXR0ZSBhYm91dCBbYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3RzXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL1N1bW1hcml6ZWRFeHBlcmltZW50L2luc3QvZG9jL1N1bW1hcml6ZWRFeHBlcmltZW50Lmh0bWwpLgoKQXMgc2hvd24gaW4gdGhlIGRpYWdyYW0sIHdlIGNhbiB1c2Ugc29tZSBvZiB0aGUgZnVuY3Rpb25zIHByb3ZpZGVkIGJ5IHRoZSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIHBhY2thZ2UgdG8gZXh0cmFjdCBkYXRhIGZyb20gb3VyIGBnZW5lX3N1bW1hcml6ZWRgIG9iamVjdC4KRm9yIGV4YW1wbGUsIGNhbGxpbmcgYHJvd0RhdGEoKWAgb24gb3VyIG9iamVjdCBzaG93cyBhbGwgdGhlIGdlbmUgaW5mb3JtYXRpb24gdGhhdCBgdHhpbWV0YWAgc2V0IHVwIQoKYGBge3J9CiMgcm93RGF0YSgpIHNob3dzIHVzIG91ciBnZW5lIGFubm90YXRpb24Kcm93RGF0YShnZW5lX3N1bW1hcml6ZWQpCmBgYAoKVGhlIGBhc3NheWAgc2xvdCBpbiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgcyBob2xkcyBkYXRhIGZyb20gdGhlIGV4cGVyaW1lbnQuCkluIHRoaXMgY2FzZSwgaXQgd2lsbCBpbmNsdWRlIG91ciBnZW5lLWxldmVsIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gc3RvcmVkIGFzIGEgZ2VuZSB4IHNhbXBsZSBtYXRyaXguCgpNdWx0aXBsZSBgYXNzYXlzYCBjYW4gYmUgc3RvcmVkIGluIGFuIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgYW5kIHdlIGNhbiB1c2UgdGhlIGBhc3NheU5hbWVzKClgIGZ1bmN0aW9uIHRvIHNlZSB3aGF0IGFzc2F5cyBhcmUgaW5jbHVkZWQgaW4gYGdlbmVfc3VtbWFyaXplZGAuCgpgYGB7ciBhc3NheS1uYW1lcywgbGl2ZSA9IFRSVUV9CmFzc2F5TmFtZXMoZ2VuZV9zdW1tYXJpemVkKQpgYGAKCklmIHdlIHdhbnQgdG8gZXh0cmFjdCBhbiBgYXNzYXlgJ3MgZGF0YSwgd2UgY2FuIHVzZSBgYXNzYXkoKWAgZnVuY3Rpb24gYW5kIHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIGFzc2F5IHdlIHdhbnQgdG8gZXh0cmFjdC4KCmBgYHtyIGFzc2F5LWNvdW50cywgbGl2ZSA9IFRSVUV9CmNvdW50c19tYXQgPC0gYXNzYXkoZ2VuZV9zdW1tYXJpemVkLCAiY291bnRzIikKYGBgCgpXZSBjYW4gdXNlIHRoZSBgY2xhc3NgIGZ1bmN0aW9uIHRvIHNlZSB3aGF0IHR5cGUgb2Ygb2JqZWN0IHRoZSBgYXNzYXkoKWAgZnVuY3Rpb24gcmV0dXJucy4KCmBgYHtyIGNsYXNzLWNvdW50cywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0eXBlIG9mIG9iamVjdCBgY291bnRzX21hdGAgaXMKY2xhc3MoY291bnRzX21hdCkKYGBgCgpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBleHRyYWN0IHRoZSBUUE0gZGF0YSAtLSBjYWxsZWQgYGFidW5kYW5jZWAgZnJvbSBgZ2VuZV9zdW1tYXJpemVkYC4KCmBgYHtyIGFzc2F5LWFidW5kYW5jZSwgbGl2ZSA9IFRSVUV9CiMgTGV0J3MgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGdlbmUtbGV2ZWwgVFBNCmhlYWQoYXNzYXkoZ2VuZV9zdW1tYXJpemVkLCAiYWJ1bmRhbmNlIikpCmBgYAoKIyMgU2F2ZSB0byBmaWxlCgpXZSBjb3VsZCB1c2UgYHJlYWRyOjp3cml0ZV90c3ZgIHRvIHNhdmUgYGNvdW50c19tYXRgIG9ubHkgYnV0IGBnZW5lX3N1bW1hcml6ZWRgIGhhcyBhIGxvdCBvZiBpbmZvcm1hdGlvbiBzdG9yZWQgaGVyZSBiZXlvbmQgdGhlIGNvdW50cywgc28gd2UgbWF5IHdhbnQgdG8gc2F2ZSBhbGwgb2YgdGhpcyB0byBhIFJEUyBvYmplY3QuCgpgYGB7ciB3cml0ZS10eGksIGxpdmUgPSBUUlVFfQojIFdyaXRlIGBnZW5lX3N1bW1hcml6ZWRgIHRvIFJEUyBvYmplY3QKcmVhZHI6OndyaXRlX3JkcyhnZW5lX3N1bW1hcml6ZWQsIGZpbGUgPSB0eGlfb3V0X2ZpbGUpCmBgYAoKV2UnbGwgaW1wb3J0IHRoaXMgd2l0aCB0aGUgYERFU2VxMmAgcGFja2FnZSBpbiB0aGUgbmV4dCBub3RlYm9vay4KCiMjIFNlc3Npb24gSW5mbwoKUmVjb3JkIHNlc3Npb24gaW5mbyBmb3IgcmVwcm9kdWNpYmlsaXR5ICYgcHJvdmVuYW5jZSBwdXJwb3Nlcy4KCmBgYHtyIHNlc3Npb25pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=
diff --git a/RNA-seq/03-gastric_cancer_exploratory-live.Rmd b/RNA-seq/03-gastric_cancer_exploratory-live.Rmd index 0277ed47..534795cf 100644 --- a/RNA-seq/03-gastric_cancer_exploratory-live.Rmd +++ b/RNA-seq/03-gastric_cancer_exploratory-live.Rmd @@ -43,7 +43,7 @@ data_dir <- file.path("data", "gastric-cancer") # directory with the tximeta processed data txi_dir <- file.path(data_dir, "txi") -txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS") +txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds") ``` We'll create a directory to hold our plots. @@ -77,8 +77,7 @@ First, let's read in the data we processed with `tximeta`. We use the tissue of origin in the design formula because that will allow us to model this variable of interest. ```{r ddset} -ddset <- DESeqDataSet(gene_summarized, - design = ~ tissue) +ddset <- DESeqDataSet(gene_summarized, design = ~tissue) ``` ### Variance stabilizing transformation @@ -91,7 +90,7 @@ Since different samples are usually sequenced to different depths, we want to tr We also want to deal with the fact that genes with low counts are also likely to have higher variance (on the log2 scale), as that could bias our clustering. To handle both of these considerations, we can calculate a Variance Stabilizing Transformation of the count data, and work with that transformed data for our analysis. -See [this section of the `DESeq2` vignette](http://bioconductor.org/packages/devel/bioc/vignettes/DESeq2/inst/doc/DESeq2.html#data-transformations-and-visualization) for more on this topic. +See [this section of the `DESeq2` vignette](https://bioconductor.org/packages/release/bioc/vignettes/DESeq2/inst/doc/DESeq2.html#data-transformations-and-visualization) for more on this topic. ```{r vst} vst_data <- vst(ddset) @@ -112,15 +111,16 @@ Visualizing PC1 and PC2 can give us insight into how different variables (e.g., ```{r plotPCA, live = TRUE} -# DESeq2 built in function is called plotPCA and we want to color points by -# tissue +# DESeq2 built in function is called plotPCA and we want to +# color points by tissue ``` Save the most recent plot to file with `ggsave` from `ggplot2` ```{r save-pdf} -# Save the PDF file +# Save plot as a PDF file +# Note that `last_plot()` is the default, but we are making it explicit here ggplot2::ggsave(pca_plot_file, plot = ggplot2::last_plot()) ``` @@ -140,8 +140,8 @@ Now we can add a new column with toy batch information and re-store the `colData ```{r add-batch} # Add batch information -sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", "batch2", - "batch1", "batch2", "batch1") +sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", + "batch2", "batch1", "batch2", "batch1") ``` If this batch information were real we would have included it with the sample metadata when we made the original `SummarizedExperiment` object with `tximeta`. @@ -149,7 +149,7 @@ We would then include it in the model stored in our DESeq2 object using the `des Here we will take a bit of a shortcut and add it directly to the `colData()` for our `vst()`-transformed data. ```{r coldata-vst, live = TRUE} -# Add coldata() with batch info to vst_data +# Add updated colData() with batch info to vst_data ``` diff --git a/RNA-seq/03-gastric_cancer_exploratory.nb.html b/RNA-seq/03-gastric_cancer_exploratory.nb.html index b4d26555..a953eeb0 100644 --- a/RNA-seq/03-gastric_cancer_exploratory.nb.html +++ b/RNA-seq/03-gastric_cancer_exploratory.nb.html @@ -3031,25 +3031,23 @@

Libraries and functions

Directories and files

- +
# Main data directory
 data_dir <- file.path("data", "gastric-cancer")
 
 # directory with the tximeta processed data
 txi_dir <- file.path(data_dir, "txi")
-txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS")
+txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds")

We’ll create a directory to hold our plots.

- +
# Create a plots directory if it does not exist yet
 plots_dir <- file.path("plots", "gastric-cancer")
-if (!dir.exists(plots_dir)) {
-  dir.create(plots_dir, recursive = TRUE)
-}
+fs::dir_create(plots_dir) @@ -3085,9 +3083,8 @@

Set up DESeq2 object

allow us to model this variable of interest.

- -
ddset <- DESeqDataSet(gene_summarized,
-                      design = ~ tissue)
+ +
ddset <- DESeqDataSet(gene_summarized, design = ~tissue)
using counts and average transcript lengths from tximeta
@@ -3115,7 +3112,7 @@

Variance stabilizing transformation

considerations, we can calculate a Variance Stabilizing Transformation of the count data, and work with that transformed data for our analysis.

-

See this +

See this section of the DESeq2 vignette for more on this topic.

@@ -3151,16 +3148,16 @@

Principal component analysis

and help us spot any technical effects (more on that below).

- -
# DESeq2 built in function is called plotPCA and we want to color points by
-# tissue
+
+
# DESeq2 built in function is called plotPCA and we want to 
+# color points by tissue
 plotPCA(vst_data, intgroup = "tissue")
using ntop=500 top features by variance
-

+

@@ -3168,8 +3165,9 @@

Principal component analysis

ggplot2

- -
# Save the PDF file
+
+
# Save plot as a PDF file
+# Note that `last_plot()` is the default, but we are making it explicit here
 ggplot2::ggsave(pca_plot_file, plot = ggplot2::last_plot())
@@ -3214,10 +3212,10 @@

A note on technical effects

the colData().

- +
# Add batch information
-sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", "batch2",
-                       "batch1", "batch2", "batch1")
+sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", + "batch2", "batch1", "batch2", "batch1")
@@ -3232,20 +3230,22 @@

A note on technical effects

vst()-transformed data.

- -
# Add coldata() with batch info to vst_data
+
+
# Add updated colData() with batch info to vst_data
 colData(vst_data) <- sample_info
- +
# PCA plot - tissue *and* batch
 # We want plotPCA to return the data so we can have more control about the plot
-pca_data <- plotPCA(vst_data,
-                    intgroup = c("tissue", "batch"),
-                    returnData = TRUE)
+pca_data <- plotPCA( + vst_data, + intgroup = c("tissue", "batch"), + returnData = TRUE +)
using ntop=500 top features by variance
@@ -3263,18 +3263,23 @@

A note on technical effects

Let’s use ggplot to visualize the first two principal components.

- +
# Color points by "batch" and use shape to indicate the tissue of origin
-ggplot2::ggplot(pca_data, ggplot2::aes(PC1, PC2,
-                                       color = batch,
-                                       shape = tissue)) +
+ggplot2::ggplot(pca_data,
+  ggplot2::aes(
+    x = PC1, 
+    y = PC2,
+    color = batch,
+    shape = tissue
+  )
+) +
   ggplot2::geom_point(size = 3) +
-  ggplot2::xlab(paste0("PC1: ", percent_var[1],"% variance")) +
-  ggplot2::ylab(paste0("PC2: ", percent_var[2],"% variance")) +
+  ggplot2::xlab(paste0("PC1: ", percent_var[1], "% variance")) +
+  ggplot2::ylab(paste0("PC2: ", percent_var[2], "% variance")) +
   ggplot2::coord_fixed()
-

+

@@ -3288,8 +3293,8 @@

Session Info

sessionInfo()
- -
R version 4.4.0 (2024-04-24)
+
+
R version 4.4.1 (2024-06-14)
 Platform: x86_64-pc-linux-gnu
 Running under: Ubuntu 22.04.4 LTS
 
@@ -3323,18 +3328,18 @@ 

Session Info

loaded via a namespace (and not attached): [1] gtable_0.3.5 xfun_0.43 bslib_0.7.0 [4] ggplot2_3.5.1 lattice_0.22-6 tzdb_0.4.0 - [7] vctrs_0.6.5 tools_4.4.0 generics_0.1.3 -[10] parallel_4.4.0 getopt_1.20.4 tibble_3.2.1 + [7] vctrs_0.6.5 tools_4.4.1 generics_0.1.3 +[10] parallel_4.4.1 getopt_1.20.4 tibble_3.2.1 [13] fansi_1.0.6 highr_0.10 pkgconfig_2.0.3 [16] Matrix_1.7-0 lifecycle_1.0.4 GenomeInfoDbData_1.2.12 -[19] farver_2.1.1 compiler_4.4.0 stringr_1.5.1 +[19] farver_2.1.1 compiler_4.4.1 stringr_1.5.1 [22] textshaping_0.3.7 munsell_0.5.1 codetools_0.2-20 [25] htmltools_0.5.8.1 sass_0.4.9 yaml_2.3.8 [28] pillar_1.9.0 crayon_1.5.2 jquerylib_0.1.4 [31] BiocParallel_1.38.0 DelayedArray_0.30.0 cachem_1.0.8 [34] abind_1.4-5 locfit_1.5-9.9 tidyselect_1.2.1 [37] digest_0.6.35 stringi_1.8.3 dplyr_1.1.4 -[40] labeling_0.4.3 fastmap_1.1.1 grid_4.4.0 +[40] labeling_0.4.3 fastmap_1.1.1 grid_4.4.1 [43] colorspace_2.1-0 cli_3.6.2 SparseArray_1.4.0 [46] magrittr_2.0.3 S4Arrays_1.4.0 utf8_1.2.4 [49] withr_3.0.0 readr_2.1.5 UCSC.utils_1.0.0 @@ -3342,12 +3347,13 @@

Session Info

[55] httr_1.4.7 ragg_1.3.0 hms_1.1.3 [58] evaluate_0.23 knitr_1.46 rlang_1.1.3 [61] Rcpp_1.0.12 glue_1.7.0 jsonlite_1.8.8 -[64] R6_2.5.1 systemfonts_1.0.6 zlibbioc_1.50.0
+[64] R6_2.5.1 systemfonts_1.0.6 fs_1.6.4 +[67] zlibbioc_1.50.0
-
LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGV4cGxvcmF0b3J5IGFuYWx5c2lzIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyMgT2JqZWN0aXZlcwoKVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0bzoKCi0gQ3JlYXRlIGEgYERFU2VxMmAgZGF0YSBzZXQgZnJvbSBhIGBTdW1tYXJpemVkRXhwZXJpbWVudGAKLSBUcmFuc2Zvcm0gUk5BLXNlcSBjb3VudCBkYXRhIHdpdGggYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbgotIENyZWF0ZSBQQ0EgcGxvdHMgdG8gZXhwbG9yZSBzdHJ1Y3R1cmUgYW1vbmcgUk5BLXNlcSBzYW1wbGVzCgotLS0KCkluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIGltcG9ydCB0aGUgZ2FzdHJpYyBjYW5jZXIgZGF0YSBhbmQgZG8gc29tZSBleHBsb3JhdG9yeQphbmFseXNlcyBhbmQgdmlzdWFsIGluc3BlY3Rpb24uCldlJ2xsIHVzZSB0aGUgW2BERVNlcTJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIHBhY2thZ2UgZm9yIHRoaXMuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCmBERVNlcTJgIGFsc28gaGFzIGFuIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkKZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlci4gX0dlbm9tZSBCaW9sb2d5Xy4gMjAxNC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeSwgbGl2ZSA9IFRSVUV9CiMgTG9hZCB0aGUgREVTZXEyIGxpYnJhcnkKbGlicmFyeShERVNlcTIpCmBgYAoKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKYGBge3IgaW5wdXQtZmlsZXN9CiMgTWFpbiBkYXRhIGRpcmVjdG9yeQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2FzdHJpYy1jYW5jZXIiKQoKIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5SRFMiKQpgYGAKCldlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXMgbm90IGV4aXN0IHlldApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJnYXN0cmljLWNhbmNlciIpCmlmICghZGlyLmV4aXN0cyhwbG90c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShwbG90c19kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgoqKk91dHB1dCoqCgpgYGB7ciBvdXRwdXQtZmlsZXMsIGxpdmUgPSBUUlVFfQojIFdlIHdpbGwgc2F2ZSBhIFBERiBjb3B5IG9mIHRoZSBQQ0EgcGxvdCB0byB0aGUgcGxvdHMgZGlyZWN0b3J5CiMgYW5kIG5hbWUgdGhlIGZpbGUgImdhc3RyaWMtY2FuY2VyX1BDX3NjYXR0ZXIucGRmIgpwY2FfcGxvdF9maWxlIDwtIGZpbGUucGF0aChwbG90c19kaXIsICJnYXN0cmljLWNhbmNlcl9QQ19zY2F0dGVyLnBkZiIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIGEgdHhpbWV0YSBvYmplY3QKCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBkYXRhIHdlIHByb2Nlc3NlZCB3aXRoIGB0eGltZXRhYC4KCmBgYHtyIHJlYWQtcmRzLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBSRFMgZmlsZSB3ZSBjcmVhdGVkIGluIHRoZSBsYXN0IG5vdGVib29rCmdlbmVfc3VtbWFyaXplZCA8LSByZWFkcjo6cmVhZF9yZHModHhpX2ZpbGUpCmBgYAoKIyMjIFNldCB1cCBERVNlcTIgb2JqZWN0CgpXZSB1c2UgdGhlIHRpc3N1ZSBvZiBvcmlnaW4gaW4gdGhlIGRlc2lnbiBmb3JtdWxhIGJlY2F1c2UgdGhhdCB3aWxsIGFsbG93IHVzIHRvIG1vZGVsIHRoaXMgdmFyaWFibGUgb2YgaW50ZXJlc3QuCgpgYGB7ciBkZHNldH0KZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gdGlzc3VlKQpgYGAKCiMjIyBWYXJpYW5jZSBzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbgoKUmF3IGNvdW50IGRhdGEgaXMgbm90IHVzdWFsbHkgc3VpdGFibGUgZm9yIHRoZSBhbGdvcml0aG1zIHdlIHVzZSBmb3IgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLCBjbHVzdGVyaW5nLCBvciBoZWF0bWFwcy4KVG8gaW1wcm92ZSB0aGlzLCB3ZSB3aWxsIHRyYW5zZm9ybSB0aGUgY291bnQgZGF0YSB0byBjcmVhdGUgYW4gZXhwcmVzc2lvbiBtZWFzdXJlIHRoYXQgaXMgYmV0dGVyIHN1aXRlZCBmb3IgdGhlc2UgYW5hbHlzZXMuClRoZSBjb3JlIHRyYW5zZm9ybWF0aW9uIHdpbGwgbWFwIHRoZSBleHByZXNzaW9uIHRvIGEgbG9nMiBzY2FsZSwgd2hpbGUgYWNjb3VudGluZyBmb3Igc29tZSBvZiB0aGUgZXhwZWN0ZWQgdmFyaWF0aW9uIGFtb25nIHNhbXBsZXMgYW5kIGdlbmVzLgoKU2luY2UgZGlmZmVyZW50IHNhbXBsZXMgYXJlIHVzdWFsbHkgc2VxdWVuY2VkIHRvIGRpZmZlcmVudCBkZXB0aHMsIHdlIHdhbnQgdG8gdHJhbnNmb3JtIG91ciBSTkEtc2VxIGNvdW50IGRhdGEgdG8gbWFrZSBkaWZmZXJlbnQgc2FtcGxlcyBtb3JlIGRpcmVjdGx5IGNvbXBhcmFibGUuCldlIGFsc28gd2FudCB0byBkZWFsIHdpdGggdGhlIGZhY3QgdGhhdCBnZW5lcyB3aXRoIGxvdyBjb3VudHMgYXJlIGFsc28gbGlrZWx5IHRvIGhhdmUgaGlnaGVyIHZhcmlhbmNlIChvbiB0aGUgbG9nMiBzY2FsZSksIGFzIHRoYXQgY291bGQgYmlhcyBvdXIgY2x1c3RlcmluZy4KVG8gaGFuZGxlIGJvdGggb2YgdGhlc2UgY29uc2lkZXJhdGlvbnMsIHdlIGNhbiBjYWxjdWxhdGUgYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgZGF0YSwgYW5kIHdvcmsgd2l0aCB0aGF0IHRyYW5zZm9ybWVkIGRhdGEgZm9yIG91ciBhbmFseXNpcy4KClNlZSBbdGhpcyBzZWN0aW9uIG9mIHRoZSBgREVTZXEyYCB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2RhdGEtdHJhbnNmb3JtYXRpb25zLWFuZC12aXN1YWxpemF0aW9uKSBmb3IgbW9yZSBvbiB0aGlzIHRvcGljLgoKYGBge3IgdnN0fQp2c3RfZGF0YSA8LSB2c3QoZGRzZXQpCmBgYAoKIyMjIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMKClByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgaXMgYSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdGVjaG5pcXVlIHRoYXQgYWxsb3dzIHVzIHRvIGlkZW50aWZ5IHRoZSBsYXJnZXN0IGNvbXBvbmVudHMgb2YgdmFyaWF0aW9uIGluIGEgY29tcGxleCBkYXRhc2V0LgpPdXIgZXhwcmVzc2lvbiBkYXRhIGNhbiBiZSB0aG91Z2h0IG9mIGFzIG1hcHBpbmcgZWFjaCBzYW1wbGUgaW4gYSBtdWx0aWRpbWVuc2lvbmFsIHNwYWNlIGRlZmluZWQgYnkgdGhlIGV4cHJlc3Npb24gbGV2ZWwgb2YgZWFjaCBnZW5lLgpUaGUgZXhwcmVzc2lvbiBvZiBtYW55IG9mIHRob3NlIGdlbmVzIGFyZSBjb3JyZWxhdGVkLCBzbyB3ZSBjYW4gb2Z0ZW4gZ2V0IGEgYmV0dGVyLCBzaW1wbGVyIHBpY3R1cmUgb2YgdGhlIGRhdGEgYnkgY29tYmluaW5nIHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRob3NlIGNvcnJlbGF0ZWQgZ2VuZXMuCgpQQ0Egcm90YXRlcyBhbmQgdHJhbnNmb3JtcyB0aGlzIHNwYWNlIHNvIHRoYXQgZWFjaCBheGlzIGlzIG5vdyBhIGNvbWJpbmF0aW9uIG9mIG11bHRpcGxlIGNvcnJlbGF0ZWQgZ2VuZXMsIG9yZGVyZWQgc28gdGhlIGZpcnN0IGF4ZXMgY2FwdHVyZSB0aGUgbW9zdCB2YXJpYXRpb24gZnJvbSB0aGUgZGF0YS4KVGhlc2UgbmV3IGF4ZXMgYXJlIHRoZSAicHJpbmNpcGFsIGNvbXBvbmVudHMuIgpJZiB3ZSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgY29tcG9uZW50cywgd2UgY2FuIG9mdGVuIGdldCBhIG5pY2Ugb3ZlcnZpZXcgb2YgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgc2FtcGxlcyBpbiB0aGUgZGF0YS4KClRoZSBgcGxvdFBDQSgpYCBmdW5jdGlvbiB3ZSB3aWxsIHVzZSBmcm9tIHRoZSBgREVTZXEyYCBwYWNrYWdlIGNhbGN1bGF0ZXMgYW5kIHBsb3RzIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDMSBhbmQgUEMyKS4KVmlzdWFsaXppbmcgUEMxIGFuZCBQQzIgY2FuIGdpdmUgdXMgaW5zaWdodCBpbnRvIGhvdyBkaWZmZXJlbnQgdmFyaWFibGVzIChlLmcuLCB0aXNzdWUgc291cmNlKSBhZmZlY3Qgb3VyIGRhdGFzZXQgYW5kIGhlbHAgdXMgc3BvdCBhbnkgdGVjaG5pY2FsIGVmZmVjdHMgKG1vcmUgb24gdGhhdCBiZWxvdykuCgoKYGBge3IgcGxvdFBDQSwgbGl2ZSA9IFRSVUV9CiMgREVTZXEyIGJ1aWx0IGluIGZ1bmN0aW9uIGlzIGNhbGxlZCBwbG90UENBIGFuZCB3ZSB3YW50IHRvIGNvbG9yIHBvaW50cyBieQojIHRpc3N1ZQpwbG90UENBKHZzdF9kYXRhLCBpbnRncm91cCA9ICJ0aXNzdWUiKQpgYGAKClNhdmUgdGhlIG1vc3QgcmVjZW50IHBsb3QgdG8gZmlsZSB3aXRoIGBnZ3NhdmVgIGZyb20gYGdncGxvdDJgCgpgYGB7ciBzYXZlLXBkZn0KIyBTYXZlIHRoZSBQREYgZmlsZQpnZ3Bsb3QyOjpnZ3NhdmUocGNhX3Bsb3RfZmlsZSwgcGxvdCA9IGdncGxvdDI6Omxhc3RfcGxvdCgpKQpgYGAKCiMjIEEgbm90ZSBvbiB0ZWNobmljYWwgZWZmZWN0cwoKV2UgZG9uJ3QgaGF2ZSBiYXRjaCBpbmZvcm1hdGlvbiAoaS5lLiwgd2hlbiB0aGUgc2FtcGxlcyB3ZXJlIHJ1bikgZm9yIHRoaXMgcGFydGljdWxhciBleHBlcmltZW50LCBidXQgbGV0J3MgaW1hZ2luZSB0aGF0IGBTUlI1ODU1NzRgIGFuZCBgU1JSNTg1NTc2YCB3ZXJlIHJ1biBzZXBhcmF0ZWx5IGZyb20gYWxsIG90aGVyIHNhbXBsZXMuCldlJ2xsIGFkZCB0aGlzIGFzIGEgbmV3ICJ0b3kiIGNvbHVtbiBpbiB0aGUgc2FtcGxlIGRhdGEgKGBjb2xEYXRhYCkuCgpgYGB7ciBleHRyYWN0LXNhbXBsZSwgbGl2ZSA9IFRSVUV9CiMgRXh0cmFjdCBjb2xEYXRhCnNhbXBsZV9pbmZvIDwtIGNvbERhdGEodnN0X2RhdGEpCgojIFByaW50IG91dCBwcmV2aWV3CnNhbXBsZV9pbmZvCmBgYAoKTm93IHdlIGNhbiBhZGQgYSBuZXcgY29sdW1uIHdpdGggdG95IGJhdGNoIGluZm9ybWF0aW9uIGFuZCByZS1zdG9yZSB0aGUgYGNvbERhdGEoKWAuCgpgYGB7ciBhZGQtYmF0Y2h9CiMgQWRkIGJhdGNoIGluZm9ybWF0aW9uCnNhbXBsZV9pbmZvJGJhdGNoIDwtIGMoImJhdGNoMSIsICJiYXRjaDEiLCAiYmF0Y2gxIiwgImJhdGNoMSIsICJiYXRjaDIiLAogICAgICAgICAgICAgICAgICAgICAgICJiYXRjaDEiLCAiYmF0Y2gyIiwgImJhdGNoMSIpCmBgYAoKSWYgdGhpcyBiYXRjaCBpbmZvcm1hdGlvbiB3ZXJlIHJlYWwgd2Ugd291bGQgaGF2ZSBpbmNsdWRlZCBpdCB3aXRoIHRoZSBzYW1wbGUgbWV0YWRhdGEgd2hlbiB3ZSBtYWRlIHRoZSBvcmlnaW5hbCBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdCB3aXRoIGB0eGltZXRhYC4KV2Ugd291bGQgdGhlbiBpbmNsdWRlIGl0IGluIHRoZSBtb2RlbCBzdG9yZWQgaW4gb3VyIERFU2VxMiBvYmplY3QgdXNpbmcgdGhlIGBkZXNpZ25gIGFyZ3VtZW50IChgZGVzaWduID0gfiB0aXNzdWUgKyBiYXRjaGApIGFuZCB3ZSB3b3VsZCByZS1ydW4gdGhlIGBERVNlcURhdGFTZXQoKWAgYW5kIGB2c3QoKWAgc3RlcHMgd2UgZGlkIGFib3ZlLgpIZXJlIHdlIHdpbGwgdGFrZSBhIGJpdCBvZiBhIHNob3J0Y3V0IGFuZCBhZGQgaXQgZGlyZWN0bHkgdG8gdGhlIGBjb2xEYXRhKClgIGZvciBvdXIgYHZzdCgpYC10cmFuc2Zvcm1lZCBkYXRhLgoKYGBge3IgY29sZGF0YS12c3QsIGxpdmUgPSBUUlVFfQojIEFkZCBjb2xkYXRhKCkgd2l0aCBiYXRjaCBpbmZvIHRvIHZzdF9kYXRhCmNvbERhdGEodnN0X2RhdGEpIDwtIHNhbXBsZV9pbmZvCmBgYAoKYGBge3IgcGxvdFBDQS0yLCBsaXZlID0gVFJVRX0KIyBQQ0EgcGxvdCAtIHRpc3N1ZSAqYW5kKiBiYXRjaAojIFdlIHdhbnQgcGxvdFBDQSB0byByZXR1cm4gdGhlIGRhdGEgc28gd2UgY2FuIGhhdmUgbW9yZSBjb250cm9sIGFib3V0IHRoZSBwbG90CnBjYV9kYXRhIDwtIHBsb3RQQ0EodnN0X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0aXNzdWUiLCAiYmF0Y2giKSwKICAgICAgICAgICAgICAgICAgICByZXR1cm5EYXRhID0gVFJVRSkKYGBgCgpgYGB7ciBwZXJjZW50X3Zhcn0KIyBIZXJlIHdlIGFyZSBzZXR0aW5nIHVwIHRoZSBwZXJjZW50IHZhcmlhbmNlIHRoYXQgd2UgYXJlIGV4dHJhY3RpbmcgZnJvbSB0aGUgYHBjYV9kYXRhYCBvYmplY3QKcGVyY2VudF92YXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FfZGF0YSwgInBlcmNlbnRWYXIiKSkKYGBgCgpMZXQncyB1c2UgZ2dwbG90IHRvIHZpc3VhbGl6ZSB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzLgoKYGBge3IgY29sb3ItYnktYmF0Y2gsIGxpdmUgPSBUUlVFfQojIENvbG9yIHBvaW50cyBieSAiYmF0Y2giIGFuZCB1c2Ugc2hhcGUgdG8gaW5kaWNhdGUgdGhlIHRpc3N1ZSBvZiBvcmlnaW4KZ2dwbG90Mjo6Z2dwbG90KHBjYV9kYXRhLCBnZ3Bsb3QyOjphZXMoUEMxLCBQQzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmF0Y2gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gdGlzc3VlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZ3Bsb3QyOjp4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50X3ZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgZ2dwbG90Mjo6eWxhYihwYXN0ZTAoIlBDMjogIiwgcGVyY2VudF92YXJbMl0sIiUgdmFyaWFuY2UiKSkgKwogIGdncGxvdDI6OmNvb3JkX2ZpeGVkKCkKYGBgCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
+
LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGV4cGxvcmF0b3J5IGFuYWx5c2lzIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyMgT2JqZWN0aXZlcwoKVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0bzoKCi0gQ3JlYXRlIGEgYERFU2VxMmAgZGF0YSBzZXQgZnJvbSBhIGBTdW1tYXJpemVkRXhwZXJpbWVudGAKLSBUcmFuc2Zvcm0gUk5BLXNlcSBjb3VudCBkYXRhIHdpdGggYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbgotIENyZWF0ZSBQQ0EgcGxvdHMgdG8gZXhwbG9yZSBzdHJ1Y3R1cmUgYW1vbmcgUk5BLXNlcSBzYW1wbGVzCgotLS0KCkluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIGltcG9ydCB0aGUgZ2FzdHJpYyBjYW5jZXIgZGF0YSBhbmQgZG8gc29tZSBleHBsb3JhdG9yeQphbmFseXNlcyBhbmQgdmlzdWFsIGluc3BlY3Rpb24uCldlJ2xsIHVzZSB0aGUgW2BERVNlcTJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIHBhY2thZ2UgZm9yIHRoaXMuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCmBERVNlcTJgIGFsc28gaGFzIGFuIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkKZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlci4gX0dlbm9tZSBCaW9sb2d5Xy4gMjAxNC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeSwgbGl2ZSA9IFRSVUV9CiMgTG9hZCB0aGUgREVTZXEyIGxpYnJhcnkKbGlicmFyeShERVNlcTIpCmBgYAoKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKYGBge3IgaW5wdXQtZmlsZXN9CiMgTWFpbiBkYXRhIGRpcmVjdG9yeQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2FzdHJpYy1jYW5jZXIiKQoKIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5yZHMiKQpgYGAKCldlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXMgbm90IGV4aXN0IHlldApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJnYXN0cmljLWNhbmNlciIpCmZzOjpkaXJfY3JlYXRlKHBsb3RzX2RpcikKYGBgCgoqKk91dHB1dCoqCgpgYGB7ciBvdXRwdXQtZmlsZXMsIGxpdmUgPSBUUlVFfQojIFdlIHdpbGwgc2F2ZSBhIFBERiBjb3B5IG9mIHRoZSBQQ0EgcGxvdCB0byB0aGUgcGxvdHMgZGlyZWN0b3J5CiMgYW5kIG5hbWUgdGhlIGZpbGUgImdhc3RyaWMtY2FuY2VyX1BDX3NjYXR0ZXIucGRmIgpwY2FfcGxvdF9maWxlIDwtIGZpbGUucGF0aChwbG90c19kaXIsICJnYXN0cmljLWNhbmNlcl9QQ19zY2F0dGVyLnBkZiIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIGEgdHhpbWV0YSBvYmplY3QKCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBkYXRhIHdlIHByb2Nlc3NlZCB3aXRoIGB0eGltZXRhYC4KCmBgYHtyIHJlYWQtcmRzLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBSRFMgZmlsZSB3ZSBjcmVhdGVkIGluIHRoZSBsYXN0IG5vdGVib29rCmdlbmVfc3VtbWFyaXplZCA8LSByZWFkcjo6cmVhZF9yZHModHhpX2ZpbGUpCmBgYAoKIyMjIFNldCB1cCBERVNlcTIgb2JqZWN0CgpXZSB1c2UgdGhlIHRpc3N1ZSBvZiBvcmlnaW4gaW4gdGhlIGRlc2lnbiBmb3JtdWxhIGJlY2F1c2UgdGhhdCB3aWxsIGFsbG93IHVzIHRvIG1vZGVsIHRoaXMgdmFyaWFibGUgb2YgaW50ZXJlc3QuCgpgYGB7ciBkZHNldH0KZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwgZGVzaWduID0gfnRpc3N1ZSkKYGBgCgojIyMgVmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24KClJhdyBjb3VudCBkYXRhIGlzIG5vdCB1c3VhbGx5IHN1aXRhYmxlIGZvciB0aGUgYWxnb3JpdGhtcyB3ZSB1c2UgZm9yIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiwgY2x1c3RlcmluZywgb3IgaGVhdG1hcHMuClRvIGltcHJvdmUgdGhpcywgd2Ugd2lsbCB0cmFuc2Zvcm0gdGhlIGNvdW50IGRhdGEgdG8gY3JlYXRlIGFuIGV4cHJlc3Npb24gbWVhc3VyZSB0aGF0IGlzIGJldHRlciBzdWl0ZWQgZm9yIHRoZXNlIGFuYWx5c2VzLgpUaGUgY29yZSB0cmFuc2Zvcm1hdGlvbiB3aWxsIG1hcCB0aGUgZXhwcmVzc2lvbiB0byBhIGxvZzIgc2NhbGUsIHdoaWxlIGFjY291bnRpbmcgZm9yIHNvbWUgb2YgdGhlIGV4cGVjdGVkIHZhcmlhdGlvbiBhbW9uZyBzYW1wbGVzIGFuZCBnZW5lcy4KClNpbmNlIGRpZmZlcmVudCBzYW1wbGVzIGFyZSB1c3VhbGx5IHNlcXVlbmNlZCB0byBkaWZmZXJlbnQgZGVwdGhzLCB3ZSB3YW50IHRvIHRyYW5zZm9ybSBvdXIgUk5BLXNlcSBjb3VudCBkYXRhIHRvIG1ha2UgZGlmZmVyZW50IHNhbXBsZXMgbW9yZSBkaXJlY3RseSBjb21wYXJhYmxlLgpXZSBhbHNvIHdhbnQgdG8gZGVhbCB3aXRoIHRoZSBmYWN0IHRoYXQgZ2VuZXMgd2l0aCBsb3cgY291bnRzIGFyZSBhbHNvIGxpa2VseSB0byBoYXZlIGhpZ2hlciB2YXJpYW5jZSAob24gdGhlIGxvZzIgc2NhbGUpLCBhcyB0aGF0IGNvdWxkIGJpYXMgb3VyIGNsdXN0ZXJpbmcuClRvIGhhbmRsZSBib3RoIG9mIHRoZXNlIGNvbnNpZGVyYXRpb25zLCB3ZSBjYW4gY2FsY3VsYXRlIGEgVmFyaWFuY2UgU3RhYmlsaXppbmcgVHJhbnNmb3JtYXRpb24gb2YgdGhlIGNvdW50IGRhdGEsIGFuZCB3b3JrIHdpdGggdGhhdCB0cmFuc2Zvcm1lZCBkYXRhIGZvciBvdXIgYW5hbHlzaXMuCgpTZWUgW3RoaXMgc2VjdGlvbiBvZiB0aGUgYERFU2VxMmAgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNkYXRhLXRyYW5zZm9ybWF0aW9ucy1hbmQtdmlzdWFsaXphdGlvbikgZm9yIG1vcmUgb24gdGhpcyB0b3BpYy4KCmBgYHtyIHZzdH0KdnN0X2RhdGEgPC0gdnN0KGRkc2V0KQpgYGAKCiMjIyBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzCgpQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIGlzIGEgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHRlY2huaXF1ZSB0aGF0IGFsbG93cyB1cyB0byBpZGVudGlmeSB0aGUgbGFyZ2VzdCBjb21wb25lbnRzIG9mIHZhcmlhdGlvbiBpbiBhIGNvbXBsZXggZGF0YXNldC4KT3VyIGV4cHJlc3Npb24gZGF0YSBjYW4gYmUgdGhvdWdodCBvZiBhcyBtYXBwaW5nIGVhY2ggc2FtcGxlIGluIGEgbXVsdGlkaW1lbnNpb25hbCBzcGFjZSBkZWZpbmVkIGJ5IHRoZSBleHByZXNzaW9uIGxldmVsIG9mIGVhY2ggZ2VuZS4KVGhlIGV4cHJlc3Npb24gb2YgbWFueSBvZiB0aG9zZSBnZW5lcyBhcmUgY29ycmVsYXRlZCwgc28gd2UgY2FuIG9mdGVuIGdldCBhIGJldHRlciwgc2ltcGxlciBwaWN0dXJlIG9mIHRoZSBkYXRhIGJ5IGNvbWJpbmluZyB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aG9zZSBjb3JyZWxhdGVkIGdlbmVzLgoKUENBIHJvdGF0ZXMgYW5kIHRyYW5zZm9ybXMgdGhpcyBzcGFjZSBzbyB0aGF0IGVhY2ggYXhpcyBpcyBub3cgYSBjb21iaW5hdGlvbiBvZiBtdWx0aXBsZSBjb3JyZWxhdGVkIGdlbmVzLCBvcmRlcmVkIHNvIHRoZSBmaXJzdCBheGVzIGNhcHR1cmUgdGhlIG1vc3QgdmFyaWF0aW9uIGZyb20gdGhlIGRhdGEuClRoZXNlIG5ldyBheGVzIGFyZSB0aGUgInByaW5jaXBhbCBjb21wb25lbnRzLiIKSWYgd2UgbG9vayBhdCB0aGUgZmlyc3QgZmV3IGNvbXBvbmVudHMsIHdlIGNhbiBvZnRlbiBnZXQgYSBuaWNlIG92ZXJ2aWV3IG9mIHJlbGF0aW9uc2hpcHMgYW1vbmcgdGhlIHNhbXBsZXMgaW4gdGhlIGRhdGEuCgpUaGUgYHBsb3RQQ0EoKWAgZnVuY3Rpb24gd2Ugd2lsbCB1c2UgZnJvbSB0aGUgYERFU2VxMmAgcGFja2FnZSBjYWxjdWxhdGVzIGFuZCBwbG90cyB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIChQQzEgYW5kIFBDMikuClZpc3VhbGl6aW5nIFBDMSBhbmQgUEMyIGNhbiBnaXZlIHVzIGluc2lnaHQgaW50byBob3cgZGlmZmVyZW50IHZhcmlhYmxlcyAoZS5nLiwgdGlzc3VlIHNvdXJjZSkgYWZmZWN0IG91ciBkYXRhc2V0IGFuZCBoZWxwIHVzIHNwb3QgYW55IHRlY2huaWNhbCBlZmZlY3RzIChtb3JlIG9uIHRoYXQgYmVsb3cpLgoKCmBgYHtyIHBsb3RQQ0EsIGxpdmUgPSBUUlVFfQojIERFU2VxMiBidWlsdCBpbiBmdW5jdGlvbiBpcyBjYWxsZWQgcGxvdFBDQSBhbmQgd2Ugd2FudCB0byAKIyBjb2xvciBwb2ludHMgYnkgdGlzc3VlCnBsb3RQQ0EodnN0X2RhdGEsIGludGdyb3VwID0gInRpc3N1ZSIpCmBgYAoKU2F2ZSB0aGUgbW9zdCByZWNlbnQgcGxvdCB0byBmaWxlIHdpdGggYGdnc2F2ZWAgZnJvbSBgZ2dwbG90MmAKCmBgYHtyIHNhdmUtcGRmfQojIFNhdmUgcGxvdCBhcyBhIFBERiBmaWxlCiMgTm90ZSB0aGF0IGBsYXN0X3Bsb3QoKWAgaXMgdGhlIGRlZmF1bHQsIGJ1dCB3ZSBhcmUgbWFraW5nIGl0IGV4cGxpY2l0IGhlcmUKZ2dwbG90Mjo6Z2dzYXZlKHBjYV9wbG90X2ZpbGUsIHBsb3QgPSBnZ3Bsb3QyOjpsYXN0X3Bsb3QoKSkKYGBgCgojIyBBIG5vdGUgb24gdGVjaG5pY2FsIGVmZmVjdHMKCldlIGRvbid0IGhhdmUgYmF0Y2ggaW5mb3JtYXRpb24gKGkuZS4sIHdoZW4gdGhlIHNhbXBsZXMgd2VyZSBydW4pIGZvciB0aGlzIHBhcnRpY3VsYXIgZXhwZXJpbWVudCwgYnV0IGxldCdzIGltYWdpbmUgdGhhdCBgU1JSNTg1NTc0YCBhbmQgYFNSUjU4NTU3NmAgd2VyZSBydW4gc2VwYXJhdGVseSBmcm9tIGFsbCBvdGhlciBzYW1wbGVzLgpXZSdsbCBhZGQgdGhpcyBhcyBhIG5ldyAidG95IiBjb2x1bW4gaW4gdGhlIHNhbXBsZSBkYXRhIChgY29sRGF0YWApLgoKYGBge3IgZXh0cmFjdC1zYW1wbGUsIGxpdmUgPSBUUlVFfQojIEV4dHJhY3QgY29sRGF0YQpzYW1wbGVfaW5mbyA8LSBjb2xEYXRhKHZzdF9kYXRhKQoKIyBQcmludCBvdXQgcHJldmlldwpzYW1wbGVfaW5mbwpgYGAKCk5vdyB3ZSBjYW4gYWRkIGEgbmV3IGNvbHVtbiB3aXRoIHRveSBiYXRjaCBpbmZvcm1hdGlvbiBhbmQgcmUtc3RvcmUgdGhlIGBjb2xEYXRhKClgLgoKYGBge3IgYWRkLWJhdGNofQojIEFkZCBiYXRjaCBpbmZvcm1hdGlvbgpzYW1wbGVfaW5mbyRiYXRjaCA8LSBjKCJiYXRjaDEiLCAiYmF0Y2gxIiwgImJhdGNoMSIsICJiYXRjaDEiLAogICAgICAgICAgICAgICAgICAgICAgICJiYXRjaDIiLCAiYmF0Y2gxIiwgImJhdGNoMiIsICJiYXRjaDEiKQpgYGAKCklmIHRoaXMgYmF0Y2ggaW5mb3JtYXRpb24gd2VyZSByZWFsIHdlIHdvdWxkIGhhdmUgaW5jbHVkZWQgaXQgd2l0aCB0aGUgc2FtcGxlIG1ldGFkYXRhIHdoZW4gd2UgbWFkZSB0aGUgb3JpZ2luYWwgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3Qgd2l0aCBgdHhpbWV0YWAuCldlIHdvdWxkIHRoZW4gaW5jbHVkZSBpdCBpbiB0aGUgbW9kZWwgc3RvcmVkIGluIG91ciBERVNlcTIgb2JqZWN0IHVzaW5nIHRoZSBgZGVzaWduYCBhcmd1bWVudCAoYGRlc2lnbiA9IH4gdGlzc3VlICsgYmF0Y2hgKSBhbmQgd2Ugd291bGQgcmUtcnVuIHRoZSBgREVTZXFEYXRhU2V0KClgIGFuZCBgdnN0KClgIHN0ZXBzIHdlIGRpZCBhYm92ZS4KSGVyZSB3ZSB3aWxsIHRha2UgYSBiaXQgb2YgYSBzaG9ydGN1dCBhbmQgYWRkIGl0IGRpcmVjdGx5IHRvIHRoZSBgY29sRGF0YSgpYCBmb3Igb3VyIGB2c3QoKWAtdHJhbnNmb3JtZWQgZGF0YS4KCmBgYHtyIGNvbGRhdGEtdnN0LCBsaXZlID0gVFJVRX0KIyBBZGQgdXBkYXRlZCBjb2xEYXRhKCkgd2l0aCBiYXRjaCBpbmZvIHRvIHZzdF9kYXRhCmNvbERhdGEodnN0X2RhdGEpIDwtIHNhbXBsZV9pbmZvCmBgYAoKYGBge3IgcGxvdFBDQS0yLCBsaXZlID0gVFJVRX0KIyBQQ0EgcGxvdCAtIHRpc3N1ZSAqYW5kKiBiYXRjaAojIFdlIHdhbnQgcGxvdFBDQSB0byByZXR1cm4gdGhlIGRhdGEgc28gd2UgY2FuIGhhdmUgbW9yZSBjb250cm9sIGFib3V0IHRoZSBwbG90CnBjYV9kYXRhIDwtIHBsb3RQQ0EoCiAgdnN0X2RhdGEsCiAgaW50Z3JvdXAgPSBjKCJ0aXNzdWUiLCAiYmF0Y2giKSwKICByZXR1cm5EYXRhID0gVFJVRQopCmBgYAoKYGBge3IgcGVyY2VudF92YXJ9CiMgSGVyZSB3ZSBhcmUgc2V0dGluZyB1cCB0aGUgcGVyY2VudCB2YXJpYW5jZSB0aGF0IHdlIGFyZSBleHRyYWN0aW5nIGZyb20gdGhlIGBwY2FfZGF0YWAgb2JqZWN0CnBlcmNlbnRfdmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhX2RhdGEsICJwZXJjZW50VmFyIikpCmBgYAoKTGV0J3MgdXNlIGdncGxvdCB0byB2aXN1YWxpemUgdGhlIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cy4KCmBgYHtyIGNvbG9yLWJ5LWJhdGNoLCBsaXZlID0gVFJVRX0KIyBDb2xvciBwb2ludHMgYnkgImJhdGNoIiBhbmQgdXNlIHNoYXBlIHRvIGluZGljYXRlIHRoZSB0aXNzdWUgb2Ygb3JpZ2luCmdncGxvdDI6OmdncGxvdChwY2FfZGF0YSwKICBnZ3Bsb3QyOjphZXMoCiAgICB4ID0gUEMxLCAKICAgIHkgPSBQQzIsCiAgICBjb2xvciA9IGJhdGNoLAogICAgc2hhcGUgPSB0aXNzdWUKICApCikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZ3Bsb3QyOjp4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50X3ZhclsxXSwgIiUgdmFyaWFuY2UiKSkgKwogIGdncGxvdDI6OnlsYWIocGFzdGUwKCJQQzI6ICIsIHBlcmNlbnRfdmFyWzJdLCAiJSB2YXJpYW5jZSIpKSArCiAgZ2dwbG90Mjo6Y29vcmRfZml4ZWQoKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKUmVjb3JkIHNlc3Npb24gaW5mbyBmb3IgcmVwcm9kdWNpYmlsaXR5ICYgcHJvdmVuYW5jZSBwdXJwb3Nlcy4KCmBgYHtyIHNlc3Npb25pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=
diff --git a/RNA-seq/04-nb_cell_line_tximeta.nb.html b/RNA-seq/04-nb_cell_line_tximeta.nb.html index e8aa4d0f..5c57acb5 100644 --- a/RNA-seq/04-nb_cell_line_tximeta.nb.html +++ b/RNA-seq/04-nb_cell_line_tximeta.nb.html @@ -2916,12 +2916,12 @@

2021

data. The quant.sf files for each sample can be found in data/NB-cell/salmon_quant/<SAMPLE>.

  • Save the tximeta output as -data/NB-cell/txi/NB-cell_tximeta.RDS. Note that +data/NB-cell/txi/NB-cell_tximeta.rds. Note that data/NB-cell/txi/ is a new directory.

  • -
    LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBhIE5ldXJvYmxhc3RvbWEgY2VsbCBsaW5lIGRhdGEgc2V0IgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgYmUgd29ya2luZyB3aXRoIFJOQS1zZXEgZGF0YSBmcm9tIG5ldXJvYmxhc3RvbWEgKE5CKSBjZWxsIGxpbmVzIGZyb20KW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykKClRoZSBjb3Vyc2UgZGlyZWN0b3JzIGhhdmUgYWxyZWFkeSBwcm9jZXNzZWQgdGhlIHJhdyBkYXRhIHVzaW5nIGBzYWxtb24gcXVhbnRgIGFuZCB0aGUgYHF1YW50LnNmYCBmaWxlcyBmb3IgZWFjaCBzYW1wbGUgY2FuIGJlIGZvdW5kIGluIGBkYXRhL05CLWNlbGwvc2FsbW9uX3F1YW50LzxTQU1QTEU+YC4KCiFbXShkaWFncmFtcy9ybmEtc2VxXzUucG5nKQoKSW4gdGhlIGdhc3RyaWMgY2FuY2VyIGV4YW1wbGUsIHdlIGltcG9ydGVkIFNhbG1vbi1wcm9jZXNzZWQgZGF0YSB3aXRoIGB0eGltZXRhYCB0byB0aGVuIHVzZSB3aXRoIGBERVNlcTJgLgpXZSB3aWxsIGFsc28gdXNlIGBERVNlcTJgIGZvciB0aGVzZSBhbmFseXNlcywgc3BlY2lmaWNhbGx5IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkluIG9yZGVyIHRvIHByZXBhcmUgdGhlIE5CIGNlbGwgbGluZSBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBtb2RpZnkgdGhlIGdhc3RyaWMgY2FuY2VyIHR4aW1ldGEgbm90ZWJvb2sgKGAwMi1nYXN0cmljX2NhbmNlcl90eGltZXRhLWxpdmUuUm1kYCkgYW5kIHNhdmUgdGhpcyBuZXcgbm90ZWJvb2sgYXMgYG5iX2NlbGxfdHhpbWV0YS5SbWRgOgoKKiBUbyBjcmVhdGUgYSBuZXcgbm90ZWJvb2ssIHNlbGVjdCBgRmlsZWAgPiBgTmV3IEZpbGVgID4gYFIgTm90ZWJvb2tgLgpUaGUgbmV3IG5vdGVib29rIHNob3VsZCBhcHBlYXIgaW4geW91ciBTb3VyY2UgUGFuZSBpbiBSU3R1ZGlvLgpTYXZlIHRoZSBuZXcgbm90ZWJvb2ssIHVzaW5nIEN0cmwrUyAoQ21kK1Mgb24gTWFjKSBvciBgRmlsZWAgPiBgU2F2ZWAsIGluIHRoZSBgdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxYCBkaXJlY3Rvcnkgd2l0aCB0aGUgbmFtZSBgbmJfY2VsbF9saW5lX3R4aW1ldGEuUm1kYC4KWW91IGNhbiBhZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCiogQWx0ZXIgdGhlIGNvZGUgZnJvbSBgMDItZ2FzdHJpY19jYW5jZXJfdHhpbWV0YS1saXZlLlJtZGAgdG8gdXNlIHRoZSBOQiBjZWxsIGxpbmUgZGF0YS4KVGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGVhY2ggc2FtcGxlIGNhbiBiZSBmb3VuZCBpbiBgZGF0YS9OQi1jZWxsL3NhbG1vbl9xdWFudC88U0FNUExFPmAuCgoqIFNhdmUgdGhlIGB0eGltZXRhYCBvdXRwdXQgYXMgYGRhdGEvTkItY2VsbC90eGkvTkItY2VsbF90eGltZXRhLlJEU2AuIE5vdGUgdGhhdCBgZGF0YS9OQi1jZWxsL3R4aS9gIGlzIGEgbmV3IGRpcmVjdG9yeS4K
    +
    LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBhIE5ldXJvYmxhc3RvbWEgY2VsbCBsaW5lIGRhdGEgc2V0IgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgYmUgd29ya2luZyB3aXRoIFJOQS1zZXEgZGF0YSBmcm9tIG5ldXJvYmxhc3RvbWEgKE5CKSBjZWxsIGxpbmVzIGZyb20KW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykKClRoZSBjb3Vyc2UgZGlyZWN0b3JzIGhhdmUgYWxyZWFkeSBwcm9jZXNzZWQgdGhlIHJhdyBkYXRhIHVzaW5nIGBzYWxtb24gcXVhbnRgIGFuZCB0aGUgYHF1YW50LnNmYCBmaWxlcyBmb3IgZWFjaCBzYW1wbGUgY2FuIGJlIGZvdW5kIGluIGBkYXRhL05CLWNlbGwvc2FsbW9uX3F1YW50LzxTQU1QTEU+YC4KCiFbXShkaWFncmFtcy9ybmEtc2VxXzUucG5nKQoKSW4gdGhlIGdhc3RyaWMgY2FuY2VyIGV4YW1wbGUsIHdlIGltcG9ydGVkIFNhbG1vbi1wcm9jZXNzZWQgZGF0YSB3aXRoIGB0eGltZXRhYCB0byB0aGVuIHVzZSB3aXRoIGBERVNlcTJgLgpXZSB3aWxsIGFsc28gdXNlIGBERVNlcTJgIGZvciB0aGVzZSBhbmFseXNlcywgc3BlY2lmaWNhbGx5IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkluIG9yZGVyIHRvIHByZXBhcmUgdGhlIE5CIGNlbGwgbGluZSBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBtb2RpZnkgdGhlIGdhc3RyaWMgY2FuY2VyIHR4aW1ldGEgbm90ZWJvb2sgKGAwMi1nYXN0cmljX2NhbmNlcl90eGltZXRhLWxpdmUuUm1kYCkgYW5kIHNhdmUgdGhpcyBuZXcgbm90ZWJvb2sgYXMgYG5iX2NlbGxfdHhpbWV0YS5SbWRgOgoKKiBUbyBjcmVhdGUgYSBuZXcgbm90ZWJvb2ssIHNlbGVjdCBgRmlsZWAgPiBgTmV3IEZpbGVgID4gYFIgTm90ZWJvb2tgLgpUaGUgbmV3IG5vdGVib29rIHNob3VsZCBhcHBlYXIgaW4geW91ciBTb3VyY2UgUGFuZSBpbiBSU3R1ZGlvLgpTYXZlIHRoZSBuZXcgbm90ZWJvb2ssIHVzaW5nIEN0cmwrUyAoQ21kK1Mgb24gTWFjKSBvciBgRmlsZWAgPiBgU2F2ZWAsIGluIHRoZSBgdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxYCBkaXJlY3Rvcnkgd2l0aCB0aGUgbmFtZSBgbmJfY2VsbF9saW5lX3R4aW1ldGEuUm1kYC4KWW91IGNhbiBhZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCiogQWx0ZXIgdGhlIGNvZGUgZnJvbSBgMDItZ2FzdHJpY19jYW5jZXJfdHhpbWV0YS1saXZlLlJtZGAgdG8gdXNlIHRoZSBOQiBjZWxsIGxpbmUgZGF0YS4KVGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGVhY2ggc2FtcGxlIGNhbiBiZSBmb3VuZCBpbiBgZGF0YS9OQi1jZWxsL3NhbG1vbl9xdWFudC88U0FNUExFPmAuCgoqIFNhdmUgdGhlIGB0eGltZXRhYCBvdXRwdXQgYXMgYGRhdGEvTkItY2VsbC90eGkvTkItY2VsbF90eGltZXRhLnJkc2AuIE5vdGUgdGhhdCBgZGF0YS9OQi1jZWxsL3R4aS9gIGlzIGEgbmV3IGRpcmVjdG9yeS4K
    diff --git a/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd b/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd index 5e9afa62..73b470b6 100644 --- a/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd +++ b/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd @@ -50,7 +50,7 @@ library(EnhancedVolcano) ```{r input-files} # directory with the tximeta processed data txi_dir <- file.path("data", "NB-cell", "txi") -txi_file <- file.path(txi_dir, "NB-cell_tximeta.RDS") +txi_file <- file.path(txi_dir, "NB-cell_tximeta.rds") ``` @@ -61,9 +61,7 @@ We'll create a results directory to hold our results. ```{r results-dir} # Create a results directory if it doesn't already exist results_dir <- file.path("results", "NB-cell") -if (!dir.exists(results_dir)) { - dir.create(results_dir, recursive = TRUE) -} +fs::dir_create(results_dir) ``` We will also need a directory to store our plots. @@ -77,7 +75,7 @@ We will also need a directory to store our plots. ```{r output-files} # RDS for the output of DESeq analysis deseq_file <- file.path(results_dir, - "NB-cell_DESeq_amplified_v_nonamplified.RDS") + "NB-cell_DESeq_amplified_v_nonamplified.rds") # DESeq2 results table deseq_df_file <- file.path(results_dir, @@ -151,8 +149,11 @@ Genes that have very low counts are not likely to yield reliable differential ex We will keep only genes with total counts of at least 10 across all samples. ```{r filter_ddset} +# create a vector of TRUE and FALSE values where +# TRUE corresponds to genes with counts of at least 10 genes_to_keep <- rowSums(counts(ddset)) >= 10 -ddset <- ddset[genes_to_keep, ] +# use which() to prevent any NAs sneaking through +ddset <- ddset[which(genes_to_keep), ] ``` @@ -210,11 +211,9 @@ So for this data set we will be using `ashr` ([Stephens 2017](https://doi.org/10 ```{r lfc_shrink} # calculate shrunken log2 fold change estimates deseq_shrunken <- lfcShrink(deseq_object, - # the coefficient we want to reestimate - coef = 2, - # We will use `ashr` for estimation - type = "ashr" - ) + coef = 2, # the coefficient we want to reestimate + type = "ashr" # We will use `ashr` for estimation +) ``` Let's compare the log2 fold change estimates from the two results tables by creating a plot. @@ -226,20 +225,23 @@ comparison_df <- data.frame( lfc_original = deseq_results$log2FoldChange, lfc_shrunken = deseq_shrunken$log2FoldChange, logmean = log10(deseq_results$baseMean) - ) +) ``` Now we can plot the original and shrunken log2 fold change values to see what happened after shrinkage. ```{r plot_comparison} ggplot(comparison_df, - aes(x = lfc_original, - y = lfc_shrunken, - color = logmean)) + + aes( + x = lfc_original, + y = lfc_shrunken, + color = logmean + ) +) + geom_point(alpha = 0.1) + theme_bw() + scale_color_viridis_c() + - coord_cartesian(xlim = c(-10,10), ylim = c(-10,10)) # zoom in on the middle + coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10)) # zoom in on the middle ``` We will now do a bit of manipulation to store the results in a data frame and add the gene symbols. @@ -279,16 +281,16 @@ Even better, it outputs a `ggplot2` object, so if we want to customize it furthe ```{r volcano} EnhancedVolcano(deseq_df, - x = 'log2FoldChange', # fold change statistic to plot - y = 'pvalue', # significance values - lab = deseq_df$gene_symbol, # labels for points - pCutoff = 1e-05, # The p value cutoff we will use (default) - FCcutoff = 1, # The fold change cutoff (default) - title = NULL, # no title - subtitle = NULL, # or subtitle - caption = NULL, # or caption - labSize = 3 # smaller labels - ) + + x = "log2FoldChange", # fold change statistic to plot + y = "pvalue", # significance values + lab = deseq_df$gene_symbol, # labels for points + pCutoff = 1e-05, # The p value cutoff we will use (default) + FCcutoff = 1, # The fold change cutoff (default) + title = NULL, # no title + subtitle = NULL, # or subtitle + caption = NULL, # or caption + labSize = 3 # smaller labels +) + # change the overall theme theme_classic() + # move the legend to the bottom diff --git a/RNA-seq/05-nb_cell_line_DESeq2.nb.html b/RNA-seq/05-nb_cell_line_DESeq2.nb.html index 75271f50..69b713f5 100644 --- a/RNA-seq/05-nb_cell_line_DESeq2.nb.html +++ b/RNA-seq/05-nb_cell_line_DESeq2.nb.html @@ -3056,10 +3056,10 @@

    Directories and files

    Input

    - +
    # directory with the tximeta processed data
     txi_dir <- file.path("data", "NB-cell", "txi")
    -txi_file <- file.path(txi_dir, "NB-cell_tximeta.RDS")
    +txi_file <- file.path(txi_dir, "NB-cell_tximeta.rds")
    @@ -3067,33 +3067,29 @@

    Directories and files

    We’ll create a results directory to hold our results.

    - +
    # Create a results directory if it doesn't already exist
     results_dir <- file.path("results", "NB-cell")
    -if (!dir.exists(results_dir)) {
    -  dir.create(results_dir, recursive = TRUE)
    -}
    +fs::dir_create(results_dir)

    We will also need a directory to store our plots.

    - +
    # Create a plots directory if it doesn't already exist
     plots_dir <- file.path("plots", "NB-cell")
    -if (!dir.exists(plots_dir)) {
    -  dir.create(plots_dir, recursive = TRUE)
    -}
    +fs::dir_create(plots_dir) - +
    # RDS for the output of DESeq analysis
     deseq_file <- file.path(results_dir,
    -                        "NB-cell_DESeq_amplified_v_nonamplified.RDS")
    +                        "NB-cell_DESeq_amplified_v_nonamplified.rds")
     
     # DESeq2 results table
     deseq_df_file <- file.path(results_dir,
    @@ -3243,11 +3239,11 @@ 

    Preparation

    DESeq Dataset creation

    - +
    # Create a DESeq2 dataset from `gene_summarized`
     # remember that `status` is the variable of interest here
     ddset <- DESeqDataSet(gene_summarized,
    -                      design = ~ status)
    + design = ~status)
    using counts and average transcript lengths from tximeta
    @@ -3267,9 +3263,12 @@

    Filtering low-expressed genes

    samples.

    - -
    genes_to_keep <- rowSums(counts(ddset)) >= 10
    -ddset <- ddset[genes_to_keep, ]
    + +
    # create a vector of TRUE and FALSE values where
    +# TRUE corresponds to genes with counts of at least 10 
    +genes_to_keep <- rowSums(counts(ddset)) >= 10
    +# use which() to prevent any NAs sneaking through
    +ddset <- ddset[which(genes_to_keep), ]
    @@ -3429,14 +3428,12 @@

    Shrinking log2 fold change estimates

    2017)

    - +
    # calculate shrunken log2 fold change estimates
     deseq_shrunken <- lfcShrink(deseq_object,
    -                            # the coefficient we want to reestimate
    -                            coef = 2,
    -                            # We will use `ashr` for estimation
    -                            type = "ashr"
    -                            )
    + coef = 2, # the coefficient we want to reestimate + type = "ashr" # We will use `ashr` for estimation +)
    using 'ashr' for LFC shrinkage. If used in published research, please cite:
    @@ -3450,12 +3447,12 @@ 

    Shrinking log2 fold change estimates

    First we will combine the results into a new data frame.

    - +
    comparison_df <- data.frame(
       lfc_original = deseq_results$log2FoldChange,
       lfc_shrunken = deseq_shrunken$log2FoldChange,
       logmean = log10(deseq_results$baseMean)
    -  )
    +)
    @@ -3463,15 +3460,18 @@

    Shrinking log2 fold change estimates

    see what happened after shrinkage.

    - +
    ggplot(comparison_df,
    -       aes(x = lfc_original,
    -           y = lfc_shrunken,
    -           color = logmean)) +
    +  aes(
    +    x = lfc_original,
    +    y = lfc_shrunken,
    +    color = logmean
    +  )
    +) +
       geom_point(alpha = 0.1) +
       theme_bw() +
       scale_color_viridis_c() +
    -  coord_cartesian(xlim = c(-10,10), ylim = c(-10,10)) # zoom in on the middle
    + coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10)) # zoom in on the middle

    @@ -3535,18 +3535,18 @@

    Making a Volcano Plot

    have used before.

    - +
    EnhancedVolcano(deseq_df,
    -                x = 'log2FoldChange', # fold change statistic to plot
    -                y = 'pvalue', # significance values
    -                lab = deseq_df$gene_symbol, # labels for points
    -                pCutoff = 1e-05, # The p value cutoff we will use (default)
    -                FCcutoff = 1, # The fold change cutoff (default)
    -                title = NULL, # no title
    -                subtitle = NULL, # or subtitle
    -                caption = NULL, # or caption
    -                labSize = 3  # smaller labels
    -                ) +
    +  x = "log2FoldChange", # fold change statistic to plot
    +  y = "pvalue", # significance values
    +  lab = deseq_df$gene_symbol, # labels for points
    +  pCutoff = 1e-05, # The p value cutoff we will use (default)
    +  FCcutoff = 1, # The fold change cutoff (default)
    +  title = NULL, # no title
    +  subtitle = NULL, # or subtitle
    +  caption = NULL, # or caption
    +  labSize = 3 # smaller labels
    +) +
       # change the overall theme
       theme_classic() +
       # move the legend to the bottom
    @@ -3578,8 +3578,8 @@ 

    Session Info

    sessionInfo()
    - -
    R version 4.4.0 (2024-04-24)
    +
    +
    R version 4.4.1 (2024-06-14)
     Platform: x86_64-pc-linux-gnu
     Running under: Ubuntu 22.04.4 LTS
     
    @@ -3615,16 +3615,16 @@ 

    Session Info

    [1] tidyselect_1.2.1 viridisLite_0.4.2 dplyr_1.1.4 [4] farver_2.1.1 fastmap_1.1.1 digest_0.6.35 [7] lifecycle_1.0.4 invgamma_1.1 magrittr_2.0.3 -[10] compiler_4.4.0 rlang_1.1.3 sass_0.4.9 -[13] tools_4.4.0 utf8_1.2.4 yaml_2.3.8 +[10] compiler_4.4.1 rlang_1.1.3 sass_0.4.9 +[13] tools_4.4.1 utf8_1.2.4 yaml_2.3.8 [16] knitr_1.46 S4Arrays_1.4.0 labeling_0.4.3 [19] bit_4.0.5 DelayedArray_0.30.0 abind_1.4-5 -[22] BiocParallel_1.38.0 withr_3.0.0 grid_4.4.0 +[22] BiocParallel_1.38.0 withr_3.0.0 grid_4.4.1 [25] fansi_1.0.6 colorspace_2.1-0 scales_1.3.0 [28] cli_3.6.2 rmarkdown_2.26 crayon_1.5.2 [31] ragg_1.3.0 generics_0.1.3 httr_1.4.7 [34] tzdb_0.4.0 getopt_1.20.4 cachem_1.0.8 -[37] stringr_1.5.1 zlibbioc_1.50.0 parallel_4.4.0 +[37] stringr_1.5.1 zlibbioc_1.50.0 parallel_4.4.1 [40] XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0 [43] jsonlite_1.8.8 hms_1.1.3 mixsqp_0.3-54 [46] bit64_4.0.5 irlba_2.3.5.1 systemfonts_1.0.6 @@ -3636,12 +3636,13 @@

    Session Info

    [64] vroom_1.6.5 evaluate_0.23 lattice_0.22-6 [67] readr_2.1.5 highr_0.10 SQUAREM_2021.1 [70] ashr_2.2-63 bslib_0.7.0 Rcpp_1.0.12 -[73] SparseArray_1.4.0 xfun_0.43 pkgconfig_2.0.3
    +[73] SparseArray_1.4.0 xfun_0.43 fs_1.6.4 +[76] pkgconfig_2.0.3
    -
    LS0tCnRpdGxlOiAiTmV1cm9ibGFzdG9tYSBDZWxsIExpbmU6IERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpdGggREVTZXEyIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2l0aCBgREVTZXEyYAotIEFwcGx5IGEgc2hyaW5rYWdlIGFsZ29yaXRobSB0byBpbXByb3ZlIGVzdGltYXRlcyBvZiBleHByZXNzaW9uIGNoYW5nZXMKLSBEcmF3IGEgdm9sY2FubyBwbG90IHdpdGggdGhlIGBFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgcGVyZm9ybSBhbiBhbmFseXNpcyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIF9NWUNOXyBhbXBsaWZpZWQgdnMuIG5vbmFtcGxpZmllZCBuZXVyb2JsYXN0b21hIGNlbGwgbGluZXMuCgpUaGVzZSBSTkEtc2VxIGRhdGEgYXJlIGZyb20gW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykuCgpNb3JlIGluZm9ybWF0aW9uIGFib3V0IERFU2VxMiBjYW4gYmUgZm91bmQgaW4gdGhlIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgpERVNlcTIgdGFrZXMgdW5ub3JtYWxpemVkIGNvdW50cyBvciBlc3RpbWF0ZWQgY291bnRzIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoqIFtFc3RpbWF0ZXMgc2l6ZSBmYWN0b3JzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvZXN0aW1hdGVTaXplRmFjdG9ycykKKiBbRXN0aW1hdGVzIGRpc3BlcnNpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ERVNlcTIvdmVyc2lvbnMvMS4xMi4zL3RvcGljcy9lc3RpbWF0ZURpc3BlcnNpb25zKQoqIE5lZ2F0aXZlIGJpbm9taWFsIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCBmaXR0aW5nIGFuZCBbV2FsZCBzdGF0aXN0aWNzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvbmJpbm9tV2FsZFRlc3QpCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCiMjIExpYnJhcmllcyBhbmQgZnVuY3Rpb25zCgpgYGB7ciBsaWJyYXJ5fQojIExvYWQgdGhlIERFU2VxMiBsaWJyYXJ5CmxpYnJhcnkoREVTZXEyKQoKIyBXZSB3aWxsIGJlIG1ha2luZyBmYW5jeSB2b2xjYW5vIHBsb3RzCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKKipJbnB1dCoqCgpgYGB7ciBpbnB1dC1maWxlc30KIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJOQi1jZWxsIiwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiTkItY2VsbF90eGltZXRhLlJEUyIpCmBgYAoKCioqT3V0cHV0KioKCldlJ2xsIGNyZWF0ZSBhIHJlc3VsdHMgZGlyZWN0b3J5IHRvIGhvbGQgb3VyIHJlc3VsdHMuCgpgYGB7ciByZXN1bHRzLWRpcn0KIyBDcmVhdGUgYSByZXN1bHRzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIk5CLWNlbGwiKQppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCldlIHdpbGwgYWxzbyBuZWVkIGEgZGlyZWN0b3J5IHRvIHN0b3JlIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJOQi1jZWxsIikKaWYgKCFkaXIuZXhpc3RzKHBsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHBsb3RzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCgpgYGB7ciBvdXRwdXQtZmlsZXN9CiMgUkRTIGZvciB0aGUgb3V0cHV0IG9mIERFU2VxIGFuYWx5c2lzCmRlc2VxX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAiTkItY2VsbF9ERVNlcV9hbXBsaWZpZWRfdl9ub25hbXBsaWZpZWQuUkRTIikKCiMgREVTZXEyIHJlc3VsdHMgdGFibGUKZGVzZXFfZGZfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQi1jZWxsX0RFU2VxX2FtcGxpZmllZF92X25vbmFtcGxpZmllZF9yZXN1bHRzLnRzdiIpCgojIFBORyBvZiB0aGUgdm9sY2FubyBwbG90CnZvbGNhbm9fZmlsZSA8LSBmaWxlLnBhdGgocGxvdHNfZGlyLCAiTkItY2VsbF92b2xjYW5vLnBuZyIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIHR4aW1ldGEgb2JqZWN0CgpGaXJzdCwgbGV0J3MgcmVhZCBpbiB0aGUgZGF0YSB3ZSBwcm9jZXNzZWQgd2l0aCBgdHhpbWV0YWAuCgojIyMjIFByZXBhcmF0aW9uCgpgYGB7ciByZWFkX3JkcywgbGl2ZSA9IFRSVUV9CiMgUmVhZCBpbiB0aGUgUkRTIGZpbGUgd2UgY3JlYXRlZCBpbiB0aGUgbGFzdCBub3RlYm9vawpnZW5lX3N1bW1hcml6ZWQgPC0gcmVhZHI6OnJlYWRfcmRzKHR4aV9maWxlKQpgYGAKCldlJ3JlIG1vc3QgaW50ZXJlc3RlZCBpbiBfTVlDTl8gYW1wbGlmaWNhdGlvbiwgd2hpY2ggd2UgaGFkIHN0b3JlZCBpbiB0aGUgYHN0YXR1c2AgY29sdW1uIG9mIHRoZSBzYW1wbGUgbWV0YWRhdGEgb2YgYGdlbmVfc3VtbWFyaXplZGAuCldoaWxlIHRoZSBzYW1wbGUgbWV0YWRhdGEgaXMgc3RvcmVkIGludGVybmFsbHkgaW4gdGhlIGBjb2xEYXRhYCBzbG90LCB0aGUgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3QgbWFrZXMgaXQgZWFzeSBmb3IgdXMgdG8gYWNjZXNzIGl0IGFzIGlmIGl0IHdlcmUganVzdCBhIGNvbHVtbiBvZiBhIGRhdGEgZnJhbWUsIHVzaW5nIHRoZSBmYW1pbGlhciBgJGAgc3ludGF4LgoKCmBgYHtyIFN0YXR1cywgbGl2ZSA9IFRSVUV9CmdlbmVfc3VtbWFyaXplZCRzdGF0dXMKYGBgCgpUaGlzIGlzIHN0b3JlZCBhcyBhIGBjaGFyYWN0ZXJgIHR5cGUsIGJ1dCB0byBnaXZlIGEgYml0IG1vcmUgaW5mb3JtYXRpb24gdG8gYERFU2VxYCwgd2Ugd2lsbCBjb252ZXJ0IHRoaXMgdG8gYSBgZmFjdG9yYC4KCmBgYHtyIHN0YXR1c19mYWN0b3IsIGxpdmUgPSBUUlVFfQpnZW5lX3N1bW1hcml6ZWQkc3RhdHVzIDwtIGFzLmZhY3RvcihnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCldlJ2xsIHdhbnQgdG8gdXNlIHRoZSAiTm9uYW1wbGlmaWVkIiBzYW1wbGVzIGFzIG91ciBfcmVmZXJlbmNlXy4KTGV0J3MgbG9vayBhdCB0aGUgYGxldmVsc2Agb2YgYHN0YXR1c2AuCgpgYGB7ciBsZXZlbHN9CmxldmVscyhnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGVzZSBhcmUgaW4gYWxwaGFiZXRpY2FsIG9yZGVyLCBzbyAiQW1wbGlmaWVkIiBzYW1wbGVzIHdvdWxkIGJlIHRoZSByZWZlcmVuY2UuCldlIGNhbiB1c2UgdGhlIGByZWxldmVsKClgIGZ1bmN0aW9uIHRvIHJlbWVkeSB0aGlzLgoKYGBge3IgcmVsZXZlbH0KZ2VuZV9zdW1tYXJpemVkJHN0YXR1cyA8LSByZWxldmVsKGdlbmVfc3VtbWFyaXplZCRzdGF0dXMsIHJlZiA9ICJOb25hbXBsaWZpZWQiKQpgYGAKCmBgYHtyIGNoZWNrLWxldmVscywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0aGUgbGV2ZWxzIGFyZSBub3cKbGV2ZWxzKGdlbmVfc3VtbWFyaXplZCRzdGF0dXMpCmBgYAoKCgojIyMjIERFU2VxIERhdGFzZXQgY3JlYXRpb24KCmBgYHtyIGRkc2V0LCBsaXZlID0gVFJVRX0KIyBDcmVhdGUgYSBERVNlcTIgZGF0YXNldCBmcm9tIGBnZW5lX3N1bW1hcml6ZWRgCiMgcmVtZW1iZXIgdGhhdCBgc3RhdHVzYCBpcyB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QgaGVyZQpkZHNldCA8LSBERVNlcURhdGFTZXQoZ2VuZV9zdW1tYXJpemVkLAogICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBzdGF0dXMpCmBgYAoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCiMjIyBGaWx0ZXJpbmcgbG93LWV4cHJlc3NlZCBnZW5lcwoKR2VuZXMgdGhhdCBoYXZlIHZlcnkgbG93IGNvdW50cyBhcmUgbm90IGxpa2VseSB0byB5aWVsZCByZWxpYWJsZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzLCBzbyB3ZSB3aWxsIGRvIHNvbWUgbGlnaHQgW3ByZS1maWx0ZXJpbmddKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3ByZS1maWx0ZXJpbmcpLgpXZSB3aWxsIGtlZXAgb25seSBnZW5lcyB3aXRoIHRvdGFsIGNvdW50cyBvZiBhdCBsZWFzdCAxMCBhY3Jvc3MgYWxsIHNhbXBsZXMuCgpgYGB7ciBmaWx0ZXJfZGRzZXR9CmdlbmVzX3RvX2tlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzZXQpKSA+PSAxMApkZHNldCA8LSBkZHNldFtnZW5lc190b19rZWVwLCBdCmBgYAoKCiMjIyBUaGUgYERFU2VxKClgIGZ1bmN0aW9uCgpXZSdsbCBub3cgdXNlIHRoZSB3cmFwcGVyIGZ1bmN0aW9uIGBERVNlcSgpYCB0byBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KQXMgbWVudGlvbmVkIGVhcmxpZXIsIHRoaXMgcGVyZm9ybXMgYSBudW1iZXIgb2Ygc3RlcHMsIGluY2x1ZGluZyBhbiBbb3V0bGllciByZW1vdmFsIHByb2NlZHVyZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2FwcHJvYWNoLXRvLWNvdW50LW91dGxpZXJzKS4KRm9yIHRoaXMgcGFydGljdWxhciBkYXRhc2V0LCB0aGVyZSBpcyBhIHByZXR0eSBsYXJnZSBudW1iZXIgb2Ygb3V0bGllcnMsIHdoaWNoIGNhbiBiZSBhIGJpdCBvZiBhIHJlZCBmbGFnLCBidXQgd2Ugd2lsbCBwcm9jZWVkIGZvciBub3cuCgpgYGB7ciBERVNlcX0KZGVzZXFfb2JqZWN0IDwtIERFU2VxKGRkc2V0KQpgYGAKCkxldCdzIHNhdmUgdGhpcyB0byBvdXIgcmVzdWx0cyBmaWxlLgoKYGBge3Igd3JpdGVfcmRzLCBsaXZlID0gVFJVRX0KIyBTYXZlIHRoZSByZXN1bHRzIGFzIGFuIFJEUwpyZWFkcjo6d3JpdGVfcmRzKGRlc2VxX29iamVjdCwgZmlsZSA9IGRlc2VxX2ZpbGUpCmBgYAoKTm93IHdlIHdpbGwgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdHMgdGFibGUuCgpgYGB7ciBkZXNlcV9yZXN1bHRzfQpkZXNlcV9yZXN1bHRzIDwtIHJlc3VsdHMoZGVzZXFfb2JqZWN0KQpkZXNlcV9yZXN1bHRzCmBgYAoKSG93IG1hbnkgZ2VuZXMgd2VyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgKEZEUiA8IDAuMDUpPwoKYGBge3IgcmVzdWx0c19zdW1tYXJ5fQpzdW1tYXJ5KGRlc2VxX3Jlc3VsdHMsIGFscGhhID0gMC4wNSkKYGBgCgoKIyMjIFNocmlua2luZyBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlcwoKVGhlIGVzdGltYXRlcyBvZiBsb2cyIGZvbGQgY2hhbmdlIGNhbGN1bGF0ZWQgYnkgYERFU2VxKClgIGFyZSBub3QgY29ycmVjdGVkIGZvciBleHByZXNzaW9uIGxldmVsLgpUaGlzIG1lYW5zIHRoYXQgd2hlbiBjb3VudHMgYXJlIHNtYWxsLCB3ZSBhcmUgbGlrZWx5IHRvIGVuZCB1cCB3aXRoIHNvbWUgbGFyZ2UgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgb3ZlcmVzdGltYXRlIHRoZSB0cnVlIGV4dGVudCBvZiB0aGUgY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucy4KCldlIGNhbiBjb3JyZWN0IHRoaXMgYnkgYXBwbHlpbmcgYSAic2hyaW5rYWdlIiBwcm9jZWR1cmUsIHdoaWNoIHdpbGwgYWRqdXN0IGxhcmdlIHZhbHVlcyB3aXRoIHNtYWxsIGNvdW50cyBkb3dud2FyZCwgd2hpbGUgcHJlc2VydmluZyB2YWx1ZXMgd2l0aCBsYXJnZXIgY291bnRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIG1vcmUgYWNjdXJhdGUuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGxmY1NocmluaygpYCBmdW5jdGlvbiwgYnV0IGZpcnN0IHdlIG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQvb3IgcG9zaXRpb24gb2YgdGhlICJjb2VmZmljaWVudCIgdGhhdCB3YXMgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIHRoZSBgcmVzdWx0c05hbWVzKClgIGZ1bmN0aW9uCgpgYGB7ciBkZXNlcV9jb2VmfQojIGdldCB0aGUgZGVzZXEgY29lZmZpY2llbnQgbmFtZXM6CnJlc3VsdHNOYW1lcyhkZXNlcV9vYmplY3QpCmBgYAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGBzdGF0dXNgIGNvZWZmaWNpZW50LCB3aGljaCBpcyBpbiBwb3NpdGlvbiAyLgoKVGhlcmUgYXJlIGEgZmV3IG9wdGlvbnMgZm9yIHRoZSBzaHJpbmthZ2UgZXN0aW1hdGlvbi4KVGhlIGRlZmF1bHQgaXMgYGFwZWdsbWAgKFtaaHUgX2V0IGFsLl8gMjAxOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnR5ODk1KSksIGJ1dCB3ZSBoYXZlIGZvdW5kIHRoYXQgdGhpcyBjYW4gYmUgc2Vuc2l0aXZlIHRvIGV4dHJlbWUgb3V0bGllcnMsIHdoaWNoIGFyZSBkZWZpbml0ZWx5IGEgZmFjdG9yIGluIHRoaXMgZGF0YSBzZXQuClNvIGZvciB0aGlzIGRhdGEgc2V0IHdlIHdpbGwgYmUgdXNpbmcgYGFzaHJgIChbU3RlcGhlbnMgMjAxN10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvc3RhdGlzdGljcy9reHcwNDEpKQoKYGBge3IgbGZjX3Nocmlua30KIyBjYWxjdWxhdGUgc2hydW5rZW4gbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMKZGVzZXFfc2hydW5rZW4gPC0gbGZjU2hyaW5rKGRlc2VxX29iamVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIGNvZWZmaWNpZW50IHdlIHdhbnQgdG8gcmVlc3RpbWF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZiA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdlIHdpbGwgdXNlIGBhc2hyYCBmb3IgZXN0aW1hdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJhc2hyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCkxldCdzIGNvbXBhcmUgdGhlIGxvZzIgZm9sZCBjaGFuZ2UgZXN0aW1hdGVzIGZyb20gdGhlIHR3byByZXN1bHRzIHRhYmxlcyBieSBjcmVhdGluZyBhIHBsb3QuCgpGaXJzdCB3ZSB3aWxsIGNvbWJpbmUgdGhlIHJlc3VsdHMgaW50byBhIG5ldyBkYXRhIGZyYW1lLgoKYGBge3IgY29tcGFyZV9zaHJpbmt9CmNvbXBhcmlzb25fZGYgPC0gZGF0YS5mcmFtZSgKICBsZmNfb3JpZ2luYWwgPSBkZXNlcV9yZXN1bHRzJGxvZzJGb2xkQ2hhbmdlLAogIGxmY19zaHJ1bmtlbiA9IGRlc2VxX3NocnVua2VuJGxvZzJGb2xkQ2hhbmdlLAogIGxvZ21lYW4gPSBsb2cxMChkZXNlcV9yZXN1bHRzJGJhc2VNZWFuKQogICkKYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlIG9yaWdpbmFsIGFuZCBzaHJ1bmtlbiBsb2cyIGZvbGQgY2hhbmdlIHZhbHVlcyB0byBzZWUgd2hhdCBoYXBwZW5lZCBhZnRlciBzaHJpbmthZ2UuCgpgYGB7ciBwbG90X2NvbXBhcmlzb259CmdncGxvdChjb21wYXJpc29uX2RmLAogICAgICAgYWVzKHggPSBsZmNfb3JpZ2luYWwsCiAgICAgICAgICAgeSA9IGxmY19zaHJ1bmtlbiwKICAgICAgICAgICBjb2xvciA9IGxvZ21lYW4pKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTEwLDEwKSwgeWxpbSA9IGMoLTEwLDEwKSkgIyB6b29tIGluIG9uIHRoZSBtaWRkbGUKYGBgCgpXZSB3aWxsIG5vdyBkbyBhIGJpdCBvZiBtYW5pcHVsYXRpb24gdG8gc3RvcmUgdGhlIHJlc3VsdHMgaW4gYSBkYXRhIGZyYW1lIGFuZCBhZGQgdGhlIGdlbmUgc3ltYm9scy4KCmBgYHtyIHJlc3VsdHNfZGF0YWZyYW1lfQojIHRoaXMgaXMgb2YgY2xhc3MgREVTZXFSZXN1bHRzIC0tIHdlIHdhbnQgYSBkYXRhIGZyYW1lCmRlc2VxX2RmIDwtIGRlc2VxX3NocnVua2VuIHw+CiAgIyBjb252ZXJ0IHRvIGEgZGF0YSBmcmFtZQogIGFzLmRhdGEuZnJhbWUoKSB8PgogICMgdGhlIGdlbmUgaWRzIHdlcmUgc3RvcmVkIGFzIHJvdyBuYW1lcyAtLSBsZXQncyB0aGVtIGEgY29sdW1uIGZvciBlYXN5IGRpc3BsYXkKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiZ2VuZV9pZCIpIHw+CiAgIyBhZGQgb24gdGhlIGdlbmUgc3ltYm9scyBmcm9tIHRoZSBvcmlnaW5hbCBkZXNlcSBvYmplY3QKICBkcGx5cjo6bXV0YXRlKGdlbmVfc3ltYm9sID0gcm93RGF0YShkZXNlcV9vYmplY3QpJGdlbmVfbmFtZSkKYGBgCgpMZXQncyBwcmludCBvdXQgdGhlIHJlc3VsdHMgdGFibGUsIHNvcnRlZCBieSBsb2cyIGZvbGQgY2hhbmdlLgpUaGUgaGlnaGVzdCB2YWx1ZXMgc2hvdWxkIGJlIGdlbmVzIG1vcmUgZXhwcmVzc2VkIGluIHRoZSBfTVlDTl8gYW1wbGlmaWVkIGNlbGwgbGluZXMuCgpgYGB7ciBzb3J0ZWRfdGFibGUsIGxpdmUgPSBUUlVFfQojIFByaW50IHRoZSB0YWJsZSBzb3J0ZWQgYnkgbG9nMkZvbGRDaGFuZ2UKZGVzZXFfZGYgfD4KICBkcGx5cjo6YXJyYW5nZShkcGx5cjo6ZGVzYyhsb2cyRm9sZENoYW5nZSkpCmBgYAoKTm93IGxldCdzIHdyaXRlIHRoZSBmdWxsIHJlc3VsdHMgdGFibGUgdG8gYSBmaWxlLgoKYGBge3Igd3JpdGVfdHN2fQpyZWFkcjo6d3JpdGVfdHN2KGRlc2VxX2RmLCBmaWxlID0gZGVzZXFfZGZfZmlsZSkKYGBgCgoKIyMgTWFraW5nIGEgVm9sY2FubyBQbG90CgpXaXRoIHRoZXNlIHNocnVua2VuIGVmZmVjdCBzaXplcywgd2Ugd2lsbCBkcmF3IGEgdm9sY2FubyBwbG90LCB1c2luZyB0aGUgW2BFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9rZXZpbmJsaWdoZS9FbmhhbmNlZFZvbGNhbm8pIHRvIG1ha2UgaXQgYSBiaXQgZWFzaWVyLgpUaGlzIHBhY2thZ2UgYXV0b21hdGljYWxseSBjb2xvciBjb2RlcyB0aGUgcG9pbnRzIGJ5IGN1dG9mZnMgZm9yIGJvdGggc2lnbmlmaWNhbmNlIGFuZCBmb2xkIGNoYW5nZSBhbmQgbGFiZWxzIG1hbnkgb2YgdGhlIHNpZ25pZmljYW50IGdlbmVzIChzdWJqZWN0IHRvIHNwYWNpbmcpLgpgRW5oYW5jZWRWb2xjYW5vYCBoYXMgbWFueSwgbWFueSBvcHRpb25zLCB3aGljaCBpcyBhIGdvb2QgdGhpbmcgaWYgeW91IGRvbid0IGxpa2UgYWxsIG9mIGl0J3MgZGVmYXVsdCBzZXR0aW5ncy4KRXZlbiBiZXR0ZXIsIGl0IG91dHB1dHMgYSBgZ2dwbG90MmAgb2JqZWN0LCBzbyBpZiB3ZSB3YW50IHRvIGN1c3RvbWl6ZSBpdCBmdXJ0aGVyLCB3ZSBjYW4gZG8gdGhhdCB3aXRoIHRoZSBzYW1lIGBnZ3Bsb3QyYCBjb21tYW5kcyB3ZSBoYXZlIHVzZWQgYmVmb3JlLgoKYGBge3Igdm9sY2Fub30KRW5oYW5jZWRWb2xjYW5vKGRlc2VxX2RmLAogICAgICAgICAgICAgICAgeCA9ICdsb2cyRm9sZENoYW5nZScsICMgZm9sZCBjaGFuZ2Ugc3RhdGlzdGljIHRvIHBsb3QKICAgICAgICAgICAgICAgIHkgPSAncHZhbHVlJywgIyBzaWduaWZpY2FuY2UgdmFsdWVzCiAgICAgICAgICAgICAgICBsYWIgPSBkZXNlcV9kZiRnZW5lX3N5bWJvbCwgIyBsYWJlbHMgZm9yIHBvaW50cwogICAgICAgICAgICAgICAgcEN1dG9mZiA9IDFlLTA1LCAjIFRoZSBwIHZhbHVlIGN1dG9mZiB3ZSB3aWxsIHVzZSAoZGVmYXVsdCkKICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwgIyBUaGUgZm9sZCBjaGFuZ2UgY3V0b2ZmIChkZWZhdWx0KQogICAgICAgICAgICAgICAgdGl0bGUgPSBOVUxMLCAjIG5vIHRpdGxlCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IE5VTEwsICMgb3Igc3VidGl0bGUKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBOVUxMLCAjIG9yIGNhcHRpb24KICAgICAgICAgICAgICAgIGxhYlNpemUgPSAzICAjIHNtYWxsZXIgbGFiZWxzCiAgICAgICAgICAgICAgICApICsKICAjIGNoYW5nZSB0aGUgb3ZlcmFsbCB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyBtb3ZlIHRoZSBsZWdlbmQgdG8gdGhlIGJvdHRvbQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCldlIHdpbGwgc2F2ZSB0aGlzIHBsb3QgdG8gYSBmaWxlIGFzIHdlbGw6CgpgYGB7ciBzYXZlX3Bsb3R9Cmdnc2F2ZSh2b2xjYW5vX2ZpbGUsIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgoKIyMgU2Vzc2lvbiBJbmZvCgpSZWNvcmQgc2Vzc2lvbiBpbmZvIGZvciByZXByb2R1Y2liaWxpdHkgJiBwcm92ZW5hbmNlIHB1cnBvc2VzLgoKYGBge3Igc2Vzc2lvbmluZm99CnNlc3Npb25JbmZvKCkKYGBgCg==
    +
    LS0tCnRpdGxlOiAiTmV1cm9ibGFzdG9tYSBDZWxsIExpbmU6IERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpdGggREVTZXEyIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2l0aCBgREVTZXEyYAotIEFwcGx5IGEgc2hyaW5rYWdlIGFsZ29yaXRobSB0byBpbXByb3ZlIGVzdGltYXRlcyBvZiBleHByZXNzaW9uIGNoYW5nZXMKLSBEcmF3IGEgdm9sY2FubyBwbG90IHdpdGggdGhlIGBFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgcGVyZm9ybSBhbiBhbmFseXNpcyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIF9NWUNOXyBhbXBsaWZpZWQgdnMuIG5vbmFtcGxpZmllZCBuZXVyb2JsYXN0b21hIGNlbGwgbGluZXMuCgpUaGVzZSBSTkEtc2VxIGRhdGEgYXJlIGZyb20gW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykuCgpNb3JlIGluZm9ybWF0aW9uIGFib3V0IERFU2VxMiBjYW4gYmUgZm91bmQgaW4gdGhlIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgpERVNlcTIgdGFrZXMgdW5ub3JtYWxpemVkIGNvdW50cyBvciBlc3RpbWF0ZWQgY291bnRzIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoqIFtFc3RpbWF0ZXMgc2l6ZSBmYWN0b3JzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvZXN0aW1hdGVTaXplRmFjdG9ycykKKiBbRXN0aW1hdGVzIGRpc3BlcnNpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ERVNlcTIvdmVyc2lvbnMvMS4xMi4zL3RvcGljcy9lc3RpbWF0ZURpc3BlcnNpb25zKQoqIE5lZ2F0aXZlIGJpbm9taWFsIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCBmaXR0aW5nIGFuZCBbV2FsZCBzdGF0aXN0aWNzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvbmJpbm9tV2FsZFRlc3QpCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCiMjIExpYnJhcmllcyBhbmQgZnVuY3Rpb25zCgpgYGB7ciBsaWJyYXJ5fQojIExvYWQgdGhlIERFU2VxMiBsaWJyYXJ5CmxpYnJhcnkoREVTZXEyKQoKIyBXZSB3aWxsIGJlIG1ha2luZyBmYW5jeSB2b2xjYW5vIHBsb3RzCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKKipJbnB1dCoqCgpgYGB7ciBpbnB1dC1maWxlc30KIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJOQi1jZWxsIiwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiTkItY2VsbF90eGltZXRhLnJkcyIpCmBgYAoKCioqT3V0cHV0KioKCldlJ2xsIGNyZWF0ZSBhIHJlc3VsdHMgZGlyZWN0b3J5IHRvIGhvbGQgb3VyIHJlc3VsdHMuCgpgYGB7ciByZXN1bHRzLWRpcn0KIyBDcmVhdGUgYSByZXN1bHRzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIk5CLWNlbGwiKQpmczo6ZGlyX2NyZWF0ZShyZXN1bHRzX2RpcikKYGBgCgpXZSB3aWxsIGFsc28gbmVlZCBhIGRpcmVjdG9yeSB0byBzdG9yZSBvdXIgcGxvdHMuCgpgYGB7ciBwbG90cy1kaXIsIGxpdmUgPSBUUlVFfQojIENyZWF0ZSBhIHBsb3RzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcGxvdHNfZGlyIDwtIGZpbGUucGF0aCgicGxvdHMiLCAiTkItY2VsbCIpCmZzOjpkaXJfY3JlYXRlKHBsb3RzX2RpcikKYGBgCgoKYGBge3Igb3V0cHV0LWZpbGVzfQojIFJEUyBmb3IgdGhlIG91dHB1dCBvZiBERVNlcSBhbmFseXNpcwpkZXNlcV9maWxlIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgIk5CLWNlbGxfREVTZXFfYW1wbGlmaWVkX3Zfbm9uYW1wbGlmaWVkLnJkcyIpCgojIERFU2VxMiByZXN1bHRzIHRhYmxlCmRlc2VxX2RmX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTkItY2VsbF9ERVNlcV9hbXBsaWZpZWRfdl9ub25hbXBsaWZpZWRfcmVzdWx0cy50c3YiKQoKIyBQTkcgb2YgdGhlIHZvbGNhbm8gcGxvdAp2b2xjYW5vX2ZpbGUgPC0gZmlsZS5wYXRoKHBsb3RzX2RpciwgIk5CLWNlbGxfdm9sY2Fuby5wbmciKQpgYGAKCiMjIERFU2VxMgoKIyMjIENyZWF0aW5nIGEgREVTZXEyIGRhdGFzZXQgZnJvbSB0eGltZXRhIG9iamVjdAoKRmlyc3QsIGxldCdzIHJlYWQgaW4gdGhlIGRhdGEgd2UgcHJvY2Vzc2VkIHdpdGggYHR4aW1ldGFgLgoKIyMjIyBQcmVwYXJhdGlvbgoKYGBge3IgcmVhZF9yZHMsIGxpdmUgPSBUUlVFfQojIFJlYWQgaW4gdGhlIFJEUyBmaWxlIHdlIGNyZWF0ZWQgaW4gdGhlIGxhc3Qgbm90ZWJvb2sKZ2VuZV9zdW1tYXJpemVkIDwtIHJlYWRyOjpyZWFkX3Jkcyh0eGlfZmlsZSkKYGBgCgpXZSdyZSBtb3N0IGludGVyZXN0ZWQgaW4gX01ZQ05fIGFtcGxpZmljYXRpb24sIHdoaWNoIHdlIGhhZCBzdG9yZWQgaW4gdGhlIGBzdGF0dXNgIGNvbHVtbiBvZiB0aGUgc2FtcGxlIG1ldGFkYXRhIG9mIGBnZW5lX3N1bW1hcml6ZWRgLgpXaGlsZSB0aGUgc2FtcGxlIG1ldGFkYXRhIGlzIHN0b3JlZCBpbnRlcm5hbGx5IGluIHRoZSBgY29sRGF0YWAgc2xvdCwgdGhlIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0IG1ha2VzIGl0IGVhc3kgZm9yIHVzIHRvIGFjY2VzcyBpdCBhcyBpZiBpdCB3ZXJlIGp1c3QgYSBjb2x1bW4gb2YgYSBkYXRhIGZyYW1lLCB1c2luZyB0aGUgZmFtaWxpYXIgYCRgIHN5bnRheC4KCgpgYGB7ciBTdGF0dXMsIGxpdmUgPSBUUlVFfQpnZW5lX3N1bW1hcml6ZWQkc3RhdHVzCmBgYAoKVGhpcyBpcyBzdG9yZWQgYXMgYSBgY2hhcmFjdGVyYCB0eXBlLCBidXQgdG8gZ2l2ZSBhIGJpdCBtb3JlIGluZm9ybWF0aW9uIHRvIGBERVNlcWAsIHdlIHdpbGwgY29udmVydCB0aGlzIHRvIGEgYGZhY3RvcmAuCgpgYGB7ciBzdGF0dXNfZmFjdG9yLCBsaXZlID0gVFJVRX0KZ2VuZV9zdW1tYXJpemVkJHN0YXR1cyA8LSBhcy5mYWN0b3IoZ2VuZV9zdW1tYXJpemVkJHN0YXR1cykKYGBgCgpXZSdsbCB3YW50IHRvIHVzZSB0aGUgIk5vbmFtcGxpZmllZCIgc2FtcGxlcyBhcyBvdXIgX3JlZmVyZW5jZV8uCkxldCdzIGxvb2sgYXQgdGhlIGBsZXZlbHNgIG9mIGBzdGF0dXNgLgoKYGBge3IgbGV2ZWxzfQpsZXZlbHMoZ2VuZV9zdW1tYXJpemVkJHN0YXR1cykKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlc2UgYXJlIGluIGFscGhhYmV0aWNhbCBvcmRlciwgc28gIkFtcGxpZmllZCIgc2FtcGxlcyB3b3VsZCBiZSB0aGUgcmVmZXJlbmNlLgpXZSBjYW4gdXNlIHRoZSBgcmVsZXZlbCgpYCBmdW5jdGlvbiB0byByZW1lZHkgdGhpcy4KCmBgYHtyIHJlbGV2ZWx9CmdlbmVfc3VtbWFyaXplZCRzdGF0dXMgPC0gcmVsZXZlbChnZW5lX3N1bW1hcml6ZWQkc3RhdHVzLCByZWYgPSAiTm9uYW1wbGlmaWVkIikKYGBgCgpgYGB7ciBjaGVjay1sZXZlbHMsIGxpdmUgPSBUUlVFfQojIENoZWNrIHdoYXQgdGhlIGxldmVscyBhcmUgbm93CmxldmVscyhnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCgoKIyMjIyBERVNlcSBEYXRhc2V0IGNyZWF0aW9uCgpgYGB7ciBkZHNldCwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgREVTZXEyIGRhdGFzZXQgZnJvbSBgZ2VuZV9zdW1tYXJpemVkYAojIHJlbWVtYmVyIHRoYXQgYHN0YXR1c2AgaXMgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IGhlcmUKZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH5zdGF0dXMpCmBgYAoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCiMjIyBGaWx0ZXJpbmcgbG93LWV4cHJlc3NlZCBnZW5lcwoKR2VuZXMgdGhhdCBoYXZlIHZlcnkgbG93IGNvdW50cyBhcmUgbm90IGxpa2VseSB0byB5aWVsZCByZWxpYWJsZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzLCBzbyB3ZSB3aWxsIGRvIHNvbWUgbGlnaHQgW3ByZS1maWx0ZXJpbmddKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3ByZS1maWx0ZXJpbmcpLgpXZSB3aWxsIGtlZXAgb25seSBnZW5lcyB3aXRoIHRvdGFsIGNvdW50cyBvZiBhdCBsZWFzdCAxMCBhY3Jvc3MgYWxsIHNhbXBsZXMuCgpgYGB7ciBmaWx0ZXJfZGRzZXR9CiMgY3JlYXRlIGEgdmVjdG9yIG9mIFRSVUUgYW5kIEZBTFNFIHZhbHVlcyB3aGVyZQojIFRSVUUgY29ycmVzcG9uZHMgdG8gZ2VuZXMgd2l0aCBjb3VudHMgb2YgYXQgbGVhc3QgMTAgCmdlbmVzX3RvX2tlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzZXQpKSA+PSAxMAojIHVzZSB3aGljaCgpIHRvIHByZXZlbnQgYW55IE5BcyBzbmVha2luZyB0aHJvdWdoCmRkc2V0IDwtIGRkc2V0W3doaWNoKGdlbmVzX3RvX2tlZXApLCBdCmBgYAoKCiMjIyBUaGUgYERFU2VxKClgIGZ1bmN0aW9uCgpXZSdsbCBub3cgdXNlIHRoZSB3cmFwcGVyIGZ1bmN0aW9uIGBERVNlcSgpYCB0byBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KQXMgbWVudGlvbmVkIGVhcmxpZXIsIHRoaXMgcGVyZm9ybXMgYSBudW1iZXIgb2Ygc3RlcHMsIGluY2x1ZGluZyBhbiBbb3V0bGllciByZW1vdmFsIHByb2NlZHVyZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2FwcHJvYWNoLXRvLWNvdW50LW91dGxpZXJzKS4KRm9yIHRoaXMgcGFydGljdWxhciBkYXRhc2V0LCB0aGVyZSBpcyBhIHByZXR0eSBsYXJnZSBudW1iZXIgb2Ygb3V0bGllcnMsIHdoaWNoIGNhbiBiZSBhIGJpdCBvZiBhIHJlZCBmbGFnLCBidXQgd2Ugd2lsbCBwcm9jZWVkIGZvciBub3cuCgpgYGB7ciBERVNlcX0KZGVzZXFfb2JqZWN0IDwtIERFU2VxKGRkc2V0KQpgYGAKCkxldCdzIHNhdmUgdGhpcyB0byBvdXIgcmVzdWx0cyBmaWxlLgoKYGBge3Igd3JpdGVfcmRzLCBsaXZlID0gVFJVRX0KIyBTYXZlIHRoZSByZXN1bHRzIGFzIGFuIFJEUwpyZWFkcjo6d3JpdGVfcmRzKGRlc2VxX29iamVjdCwgZmlsZSA9IGRlc2VxX2ZpbGUpCmBgYAoKTm93IHdlIHdpbGwgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdHMgdGFibGUuCgpgYGB7ciBkZXNlcV9yZXN1bHRzfQpkZXNlcV9yZXN1bHRzIDwtIHJlc3VsdHMoZGVzZXFfb2JqZWN0KQpkZXNlcV9yZXN1bHRzCmBgYAoKSG93IG1hbnkgZ2VuZXMgd2VyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgKEZEUiA8IDAuMDUpPwoKYGBge3IgcmVzdWx0c19zdW1tYXJ5fQpzdW1tYXJ5KGRlc2VxX3Jlc3VsdHMsIGFscGhhID0gMC4wNSkKYGBgCgoKIyMjIFNocmlua2luZyBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlcwoKVGhlIGVzdGltYXRlcyBvZiBsb2cyIGZvbGQgY2hhbmdlIGNhbGN1bGF0ZWQgYnkgYERFU2VxKClgIGFyZSBub3QgY29ycmVjdGVkIGZvciBleHByZXNzaW9uIGxldmVsLgpUaGlzIG1lYW5zIHRoYXQgd2hlbiBjb3VudHMgYXJlIHNtYWxsLCB3ZSBhcmUgbGlrZWx5IHRvIGVuZCB1cCB3aXRoIHNvbWUgbGFyZ2UgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgb3ZlcmVzdGltYXRlIHRoZSB0cnVlIGV4dGVudCBvZiB0aGUgY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucy4KCldlIGNhbiBjb3JyZWN0IHRoaXMgYnkgYXBwbHlpbmcgYSAic2hyaW5rYWdlIiBwcm9jZWR1cmUsIHdoaWNoIHdpbGwgYWRqdXN0IGxhcmdlIHZhbHVlcyB3aXRoIHNtYWxsIGNvdW50cyBkb3dud2FyZCwgd2hpbGUgcHJlc2VydmluZyB2YWx1ZXMgd2l0aCBsYXJnZXIgY291bnRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIG1vcmUgYWNjdXJhdGUuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGxmY1NocmluaygpYCBmdW5jdGlvbiwgYnV0IGZpcnN0IHdlIG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQvb3IgcG9zaXRpb24gb2YgdGhlICJjb2VmZmljaWVudCIgdGhhdCB3YXMgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIHRoZSBgcmVzdWx0c05hbWVzKClgIGZ1bmN0aW9uCgpgYGB7ciBkZXNlcV9jb2VmfQojIGdldCB0aGUgZGVzZXEgY29lZmZpY2llbnQgbmFtZXM6CnJlc3VsdHNOYW1lcyhkZXNlcV9vYmplY3QpCmBgYAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGBzdGF0dXNgIGNvZWZmaWNpZW50LCB3aGljaCBpcyBpbiBwb3NpdGlvbiAyLgoKVGhlcmUgYXJlIGEgZmV3IG9wdGlvbnMgZm9yIHRoZSBzaHJpbmthZ2UgZXN0aW1hdGlvbi4KVGhlIGRlZmF1bHQgaXMgYGFwZWdsbWAgKFtaaHUgX2V0IGFsLl8gMjAxOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnR5ODk1KSksIGJ1dCB3ZSBoYXZlIGZvdW5kIHRoYXQgdGhpcyBjYW4gYmUgc2Vuc2l0aXZlIHRvIGV4dHJlbWUgb3V0bGllcnMsIHdoaWNoIGFyZSBkZWZpbml0ZWx5IGEgZmFjdG9yIGluIHRoaXMgZGF0YSBzZXQuClNvIGZvciB0aGlzIGRhdGEgc2V0IHdlIHdpbGwgYmUgdXNpbmcgYGFzaHJgIChbU3RlcGhlbnMgMjAxN10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvc3RhdGlzdGljcy9reHcwNDEpKQoKYGBge3IgbGZjX3Nocmlua30KIyBjYWxjdWxhdGUgc2hydW5rZW4gbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMKZGVzZXFfc2hydW5rZW4gPC0gbGZjU2hyaW5rKGRlc2VxX29iamVjdCwKICBjb2VmID0gMiwgIyB0aGUgY29lZmZpY2llbnQgd2Ugd2FudCB0byByZWVzdGltYXRlCiAgdHlwZSA9ICJhc2hyIiAjIFdlIHdpbGwgdXNlIGBhc2hyYCBmb3IgZXN0aW1hdGlvbgopCmBgYAoKTGV0J3MgY29tcGFyZSB0aGUgbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMgZnJvbSB0aGUgdHdvIHJlc3VsdHMgdGFibGVzIGJ5IGNyZWF0aW5nIGEgcGxvdC4KCkZpcnN0IHdlIHdpbGwgY29tYmluZSB0aGUgcmVzdWx0cyBpbnRvIGEgbmV3IGRhdGEgZnJhbWUuCgpgYGB7ciBjb21wYXJlX3Nocmlua30KY29tcGFyaXNvbl9kZiA8LSBkYXRhLmZyYW1lKAogIGxmY19vcmlnaW5hbCA9IGRlc2VxX3Jlc3VsdHMkbG9nMkZvbGRDaGFuZ2UsCiAgbGZjX3NocnVua2VuID0gZGVzZXFfc2hydW5rZW4kbG9nMkZvbGRDaGFuZ2UsCiAgbG9nbWVhbiA9IGxvZzEwKGRlc2VxX3Jlc3VsdHMkYmFzZU1lYW4pCikKYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlIG9yaWdpbmFsIGFuZCBzaHJ1bmtlbiBsb2cyIGZvbGQgY2hhbmdlIHZhbHVlcyB0byBzZWUgd2hhdCBoYXBwZW5lZCBhZnRlciBzaHJpbmthZ2UuCgpgYGB7ciBwbG90X2NvbXBhcmlzb259CmdncGxvdChjb21wYXJpc29uX2RmLAogIGFlcygKICAgIHggPSBsZmNfb3JpZ2luYWwsCiAgICB5ID0gbGZjX3NocnVua2VuLAogICAgY29sb3IgPSBsb2dtZWFuCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMTAsIDEwKSwgeWxpbSA9IGMoLTEwLCAxMCkpICMgem9vbSBpbiBvbiB0aGUgbWlkZGxlCmBgYAoKV2Ugd2lsbCBub3cgZG8gYSBiaXQgb2YgbWFuaXB1bGF0aW9uIHRvIHN0b3JlIHRoZSByZXN1bHRzIGluIGEgZGF0YSBmcmFtZSBhbmQgYWRkIHRoZSBnZW5lIHN5bWJvbHMuCgpgYGB7ciByZXN1bHRzX2RhdGFmcmFtZX0KIyB0aGlzIGlzIG9mIGNsYXNzIERFU2VxUmVzdWx0cyAtLSB3ZSB3YW50IGEgZGF0YSBmcmFtZQpkZXNlcV9kZiA8LSBkZXNlcV9zaHJ1bmtlbiB8PgogICMgY29udmVydCB0byBhIGRhdGEgZnJhbWUKICBhcy5kYXRhLmZyYW1lKCkgfD4KICAjIHRoZSBnZW5lIGlkcyB3ZXJlIHN0b3JlZCBhcyByb3cgbmFtZXMgLS0gbGV0J3MgdGhlbSBhIGNvbHVtbiBmb3IgZWFzeSBkaXNwbGF5CiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gImdlbmVfaWQiKSB8PgogICMgYWRkIG9uIHRoZSBnZW5lIHN5bWJvbHMgZnJvbSB0aGUgb3JpZ2luYWwgZGVzZXEgb2JqZWN0CiAgZHBseXI6Om11dGF0ZShnZW5lX3N5bWJvbCA9IHJvd0RhdGEoZGVzZXFfb2JqZWN0KSRnZW5lX25hbWUpCmBgYAoKTGV0J3MgcHJpbnQgb3V0IHRoZSByZXN1bHRzIHRhYmxlLCBzb3J0ZWQgYnkgbG9nMiBmb2xkIGNoYW5nZS4KVGhlIGhpZ2hlc3QgdmFsdWVzIHNob3VsZCBiZSBnZW5lcyBtb3JlIGV4cHJlc3NlZCBpbiB0aGUgX01ZQ05fIGFtcGxpZmllZCBjZWxsIGxpbmVzLgoKYGBge3Igc29ydGVkX3RhYmxlLCBsaXZlID0gVFJVRX0KIyBQcmludCB0aGUgdGFibGUgc29ydGVkIGJ5IGxvZzJGb2xkQ2hhbmdlCmRlc2VxX2RmIHw+CiAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MobG9nMkZvbGRDaGFuZ2UpKQpgYGAKCk5vdyBsZXQncyB3cml0ZSB0aGUgZnVsbCByZXN1bHRzIHRhYmxlIHRvIGEgZmlsZS4KCmBgYHtyIHdyaXRlX3Rzdn0KcmVhZHI6OndyaXRlX3RzdihkZXNlcV9kZiwgZmlsZSA9IGRlc2VxX2RmX2ZpbGUpCmBgYAoKCiMjIE1ha2luZyBhIFZvbGNhbm8gUGxvdAoKV2l0aCB0aGVzZSBzaHJ1bmtlbiBlZmZlY3Qgc2l6ZXMsIHdlIHdpbGwgZHJhdyBhIHZvbGNhbm8gcGxvdCwgdXNpbmcgdGhlIFtgRW5oYW5jZWRWb2xjYW5vYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20va2V2aW5ibGlnaGUvRW5oYW5jZWRWb2xjYW5vKSB0byBtYWtlIGl0IGEgYml0IGVhc2llci4KVGhpcyBwYWNrYWdlIGF1dG9tYXRpY2FsbHkgY29sb3IgY29kZXMgdGhlIHBvaW50cyBieSBjdXRvZmZzIGZvciBib3RoIHNpZ25pZmljYW5jZSBhbmQgZm9sZCBjaGFuZ2UgYW5kIGxhYmVscyBtYW55IG9mIHRoZSBzaWduaWZpY2FudCBnZW5lcyAoc3ViamVjdCB0byBzcGFjaW5nKS4KYEVuaGFuY2VkVm9sY2Fub2AgaGFzIG1hbnksIG1hbnkgb3B0aW9ucywgd2hpY2ggaXMgYSBnb29kIHRoaW5nIGlmIHlvdSBkb24ndCBsaWtlIGFsbCBvZiBpdCdzIGRlZmF1bHQgc2V0dGluZ3MuCkV2ZW4gYmV0dGVyLCBpdCBvdXRwdXRzIGEgYGdncGxvdDJgIG9iamVjdCwgc28gaWYgd2Ugd2FudCB0byBjdXN0b21pemUgaXQgZnVydGhlciwgd2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgc2FtZSBgZ2dwbG90MmAgY29tbWFuZHMgd2UgaGF2ZSB1c2VkIGJlZm9yZS4KCmBgYHtyIHZvbGNhbm99CkVuaGFuY2VkVm9sY2FubyhkZXNlcV9kZiwKICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwgIyBmb2xkIGNoYW5nZSBzdGF0aXN0aWMgdG8gcGxvdAogIHkgPSAicHZhbHVlIiwgIyBzaWduaWZpY2FuY2UgdmFsdWVzCiAgbGFiID0gZGVzZXFfZGYkZ2VuZV9zeW1ib2wsICMgbGFiZWxzIGZvciBwb2ludHMKICBwQ3V0b2ZmID0gMWUtMDUsICMgVGhlIHAgdmFsdWUgY3V0b2ZmIHdlIHdpbGwgdXNlIChkZWZhdWx0KQogIEZDY3V0b2ZmID0gMSwgIyBUaGUgZm9sZCBjaGFuZ2UgY3V0b2ZmIChkZWZhdWx0KQogIHRpdGxlID0gTlVMTCwgIyBubyB0aXRsZQogIHN1YnRpdGxlID0gTlVMTCwgIyBvciBzdWJ0aXRsZQogIGNhcHRpb24gPSBOVUxMLCAjIG9yIGNhcHRpb24KICBsYWJTaXplID0gMyAjIHNtYWxsZXIgbGFiZWxzCikgKwogICMgY2hhbmdlIHRoZSBvdmVyYWxsIHRoZW1lCiAgdGhlbWVfY2xhc3NpYygpICsKICAjIG1vdmUgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKV2Ugd2lsbCBzYXZlIHRoaXMgcGxvdCB0byBhIGZpbGUgYXMgd2VsbDoKCmBgYHtyIHNhdmVfcGxvdH0KZ2dzYXZlKHZvbGNhbm9fZmlsZSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
    diff --git a/RNA-seq/06-openpbta_heatmap-live.Rmd b/RNA-seq/06-openpbta_heatmap-live.Rmd index 7de8399a..8da2be99 100644 --- a/RNA-seq/06-openpbta_heatmap-live.Rmd +++ b/RNA-seq/06-openpbta_heatmap-live.Rmd @@ -20,7 +20,7 @@ This notebook will demonstrate how to: --- In this notebook, we cluster RNA-seq data from the Open Pediatric Brain Tumor Atlas (OpenPBTA) project and create a heatmap. -OpenPBTA is a collaborative project organized by the CCDL and the Center for Data-Driven Discovery in Biomedicine (D3b) at the Children's Hospital of Philadelphia conducted openly on GitHub. +OpenPBTA is a collaborative project organized by the Data Lab and the Center for Data-Driven Discovery in Biomedicine (D3b) at the Children's Hospital of Philadelphia conducted openly on GitHub. You can read more about the project [here](https://github.com/alexslemonade/openpbta-analysis/#openpbta-analysis). @@ -64,7 +64,7 @@ We have stored the data we'll use in this notebook in `data/open-pbta`. histologies_file <- file.path(data_dir, "pbta-histologies-subset.tsv") # The RNA-seq counts table -rnaseq_file = file.path(data_dir, "pbta-rsem-expected_count-subset.rds") +rnaseq_file <- file.path(data_dir, "pbta-rsem-expected_count-subset.rds") ``` #### Output files @@ -302,7 +302,7 @@ Heatmap(zscores_mat, name = "z-score") ``` -`ComplexHeatmap` gives a few warning here, but nothing to be concerned about. +`ComplexHeatmap` gives a few warnings here, but nothing to be concerned about. One is complaining that the color scale may be truncated relative to our data. The other warns that our very large heatmap itself is being drawn at a lower resolution to speed things up. diff --git a/RNA-seq/06-openpbta_heatmap.nb.html b/RNA-seq/06-openpbta_heatmap.nb.html index c27172c4..d6e6cfa8 100644 --- a/RNA-seq/06-openpbta_heatmap.nb.html +++ b/RNA-seq/06-openpbta_heatmap.nb.html @@ -2920,26 +2920,135 @@

    Set up

    Libraries

    - -
    # We will manipulate RNASeq data with DESeq2 at the start
    + +
    # We will manipulate RNASeq data with DESeq2 at the start
    +library(DESeq2)
    - -
    Warning message:
    -replacing previous import ‘S4Arrays::makeNindexFromArrayViewport’ by ‘DelayedArray::makeNindexFromArrayViewport’ when loading ‘SummarizedExperiment’ 
    - - -
    library(DESeq2)
    -
    -# Then we'll be doing a bit of data wrangling with the Tidyverse
    +
    +
    Loading required package: S4Vectors
    + + +
    Loading required package: stats4
    + + +
    Loading required package: BiocGenerics
    + + +
    
    +Attaching package: 'BiocGenerics'
    + + +
    The following objects are masked from 'package:stats':
    +
    +    IQR, mad, sd, var, xtabs
    + + +
    The following objects are masked from 'package:base':
    +
    +    anyDuplicated, aperm, append, as.data.frame, basename, cbind,
    +    colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
    +    get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
    +    match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    +    Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
    +    tapply, union, unique, unsplit, which.max, which.min
    + + +
    
    +Attaching package: 'S4Vectors'
    + + +
    The following object is masked from 'package:utils':
    +
    +    findMatches
    + + +
    The following objects are masked from 'package:base':
    +
    +    expand.grid, I, unname
    + + +
    Loading required package: IRanges
    + + +
    Loading required package: GenomicRanges
    + + +
    Loading required package: GenomeInfoDb
    + + +
    Loading required package: SummarizedExperiment
    + + +
    Loading required package: MatrixGenerics
    + + +
    Loading required package: matrixStats
    + + +
    
    +Attaching package: 'MatrixGenerics'
    + + +
    The following objects are masked from 'package:matrixStats':
    +
    +    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
    +    colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
    +    colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
    +    colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
    +    colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
    +    colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
    +    colWeightedMeans, colWeightedMedians, colWeightedSds,
    +    colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
    +    rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    +    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
    +    rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
    +    rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
    +    rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
    +    rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
    +    rowWeightedSds, rowWeightedVars
    + + +
    Loading required package: Biobase
    + + +
    Welcome to Bioconductor
    +
    +    Vignettes contain introductory material; view with
    +    'browseVignettes()'. To cite Bioconductor, see
    +    'citation("Biobase")', and for packages 'citation("pkgname")'.
    + + +
    
    +Attaching package: 'Biobase'
    + + +
    The following object is masked from 'package:MatrixGenerics':
    +
    +    rowMedians
    + + +
    The following objects are masked from 'package:matrixStats':
    +
    +    anyMissing, rowMedians
    + + +
    Warning: replacing previous import 'S4Arrays::makeNindexFromArrayViewport' by
    +'DelayedArray::makeNindexFromArrayViewport' when loading 'SummarizedExperiment'
    + + +
    # Then we'll be doing a bit of data wrangling with the Tidyverse
     library(tidyverse)
    - -
    ── Attaching core tidyverse packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
    +
    +
    ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
     ✔ dplyr     1.1.4     ✔ readr     2.1.5
     ✔ forcats   1.0.0     ✔ stringr   1.5.1
     ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
     ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
    -✔ purrr     1.0.2     ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
    +✔ purrr     1.0.2     
    + + +
    ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
     ✖ lubridate::%within%() masks IRanges::%within%()
     ✖ dplyr::collapse()     masks IRanges::collapse()
     ✖ dplyr::combine()      masks Biobase::combine(), BiocGenerics::combine()
    @@ -2955,14 +3064,14 @@ 

    Libraries

    ✖ lubridate::second() masks S4Vectors::second() ✖ lubridate::second<-() masks S4Vectors::second<-() ✖ dplyr::slice() masks IRanges::slice() -ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
    - - +ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
    + +
    # ComplexHeatmap is the package we'll use for making a heatmap
     # It will do the hierarchical clustering for us as well
     library(ComplexHeatmap)
    - +
    Loading required package: grid
     ========================================
     ComplexHeatmap version 2.20.0
    @@ -2982,7 +3091,7 @@ 

    Libraries

    This message can be suppressed by: suppressPackageStartupMessages(library(ComplexHeatmap)) ========================================
    - + @@ -2992,7 +3101,7 @@

    Directories and files

    data/open-pbta.

    - +
    data_dir <- file.path("data", "open-pbta")
     
     # We'll store the heatmap in plots/open-pbta - create directory if it doesn't exist yet
    @@ -3005,7 +3114,7 @@ 

    Directories and files

    Input files

    - +
    # The metadata describing the samples
     histologies_file <- file.path(data_dir, "pbta-histologies-subset.tsv")
     
    @@ -3019,7 +3128,7 @@ 

    Input files

    Output files

    - +
    heatmap_file <- file.path(plots_dir,
                               "common_histologies_high_variance_heatmap.png")
    @@ -3035,17 +3144,19 @@

    Metadata

    Let’s read in the metadata file file and take a look at the data.

    - +
    histologies_df <- read_tsv(histologies_file)
    - -
    Rows: 607 Columns: 33── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    +
    +
    Rows: 607 Columns: 33
    +── Column specification ────────────────────────────────────────────────────────
     Delimiter: "\t"
    -chr (31): Kids_First_Biospecimen_ID, CNS_region, sample_id, aliquot_id, Kids_First_Participant_ID, experimental_strategy, sample_type, composition, tumor_descriptor, primary_site, reported_gender, race...
    +chr (31): Kids_First_Biospecimen_ID, CNS_region, sample_id, aliquot_id, Kids...
     dbl  (2): OS_days, age_last_update_days
    +
     ℹ Use `spec()` to retrieve the full column specification for this data.
     ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
    - +

    Use the chunk below to explore the metadata data frame.

    @@ -3062,7 +3173,7 @@

    Metadata

    the Tidyverse.

    - +
    histology_count_df <- histologies_df |>
       # Count how many samples are in each short_histology and name the column
       # with that number n
    @@ -3073,13 +3184,11 @@ 

    Metadata

    histology_count_df
    -
    - @@ -3088,7 +3197,7 @@

    RNA-seq data

    Read in the expression count matrix (stored as a data frame).

    - +
    # Read in and examine the RNA-seq data
     rnaseq_exp <- read_rds(rnaseq_file)
    @@ -3104,7 +3213,7 @@

    Convert and round

    convert from a data frame to a matrix.

    - +
    rnaseq_mat <- rnaseq_exp |>
       # move gene_id to the rownames
       tibble::column_to_rownames("gene_id") |>
    @@ -3128,7 +3237,7 @@ 

    Variance Stabilizing Transformation

    matrix.

    - +
    all.equal(histologies_df$Kids_First_Biospecimen_ID,
               colnames(rnaseq_mat))
    @@ -3143,21 +3252,21 @@

    Variance Stabilizing Transformation

    experimental design at this stage.

    - +
    ddset <- DESeqDataSetFromMatrix(rnaseq_mat,
                                     colData = histologies_df,
                                     design = ~ 1) # don't store an experimental design
    - +
    converting counts to integer mode
    - +

    We will again remove low count genes, as they are not likely to be informative.

    - +
    genes_to_keep <- rowSums(counts(ddset)) >= 10
     ddset <- ddset[genes_to_keep, ]
    @@ -3167,7 +3276,7 @@

    Variance Stabilizing Transformation

    results in a new object.

    - +
    # apply variance stabilizing transformation
     vst_data <- vst(ddset, blind = TRUE)
    @@ -3178,7 +3287,7 @@

    Variance Stabilizing Transformation

    which we can extract with assay().

    - +
    # extract transformed data
     expr_mat <- assay(vst_data)
    @@ -3187,7 +3296,7 @@

    Variance Stabilizing Transformation

    What are the dimensions of this transformed RNA-seq data matrix?

    - +
    dim(expr_mat)
    @@ -3205,7 +3314,7 @@

    Variance Stabilizing Transformation

    then take the genes in the top 10%.

    - +
    # Calculate variance from the expression data
     gene_variance <- matrixStats::rowVars(expr_mat)
     
    @@ -3218,15 +3327,19 @@ 

    Variance Stabilizing Transformation

    # What does a row index look like? head(high_variance_index)
    - -
           ENSG00000000971.15_CFH     ENSG00000001617.11_SEMA3F       ENSG00000002586.18_CD99 ENSG00000002586.18_PAR_Y_CD99      ENSG00000002587.9_HS3ST1      ENSG00000002745.12_WNT16 
    -                            7                            15                            24                            25                            26                            28 
    + +
           ENSG00000000971.15_CFH     ENSG00000001617.11_SEMA3F 
    +                            7                            15 
    +      ENSG00000002586.18_CD99 ENSG00000002586.18_PAR_Y_CD99 
    +                           24                            25 
    +     ENSG00000002587.9_HS3ST1      ENSG00000002745.12_WNT16 
    +                           26                            28 
    - +
    # Get a matrix that is subset to just the high variance genes
     high_var_mat <- expr_mat[high_variance_index, ]
    @@ -3243,7 +3356,7 @@

    Annotation

    called annotation, or HeatmapAnnotation, specifically.

    - +
    sample_annotation_df <- histologies_df |>
       # Select only the columns that we'll use
       select(Kids_First_Biospecimen_ID,
    @@ -3254,13 +3367,11 @@ 

    Annotation

    # Let's examine these columns sample_annotation_df
    -
    -

    ComplexHeatmap is going to want the data frame we @@ -3268,7 +3379,7 @@

    Annotation

    up.

    - +
    sample_annotation_df <- sample_annotation_df |>
       tibble::column_to_rownames("Kids_First_Biospecimen_ID")
    @@ -3281,7 +3392,7 @@

    Annotation

    columns.

    - +
    # The Okabe Ito palette is recommended for those with color vision deficiencies
     histology_colors <- palette.colors(palette = "Okabe-Ito")[2:5]
     # `palette.colors()` returns a named vector, which can cause trouble
    @@ -3316,7 +3427,7 @@ 

    Annotation

    nicer to look at than the raw columns names.

    - +
    column_annotation <- HeatmapAnnotation(
       df = sample_annotation_df,
       col = sample_annotation_colors,
    @@ -3336,7 +3447,7 @@ 

    Values for display

    mean of 0 and a standard deviation of 1.

    - +
    zscores_mat <-
       (high_var_mat - rowMeans(high_var_mat)) / matrixStats::rowSds(high_var_mat)
    @@ -3352,7 +3463,7 @@

    Heatmap itself!

    bars.

    - +
    Heatmap(zscores_mat,
             # The distance metric used for clustering the rows
             # This is different from the default (Euclidean)
    @@ -3372,17 +3483,23 @@ 

    Heatmap itself!

    # of the cells of the heatmap itself name = "z-score")
    - -
    The automatically generated colors map from the minus and plus 99^th of the absolute values in the matrix. There are outliers in the matrix whose patterns might be hidden by this
    -color mapping. You can manually set the color to `col` argument.
    +
    +
    The automatically generated colors map from the minus and plus 99^th of
    +the absolute values in the matrix. There are outliers in the matrix
    +whose patterns might be hidden by this color mapping. You can manually
    +set the color to `col` argument.
     
    -Use `suppressMessages()` to turn off this message.
    -`use_raster` is automatically set to TRUE for a matrix with more than 2000 rows. You can control `use_raster` argument by explicitly setting TRUE/FALSE to it.
    +Use `suppressMessages()` to turn off this message.
    + + +
    `use_raster` is automatically set to TRUE for a matrix with more than
    +2000 rows. You can control `use_raster` argument by explicitly setting
    +TRUE/FALSE to it.
     
     Set `ht_opt$message = FALSE` to turn off this message.
    - - -

    + + +

    @@ -3397,7 +3514,7 @@

    Heatmap itself!

    output.

    - +
    # Open PNG plot device
     png(filename = heatmap_file,
         width = 11,
    @@ -3416,19 +3533,21 @@ 

    Heatmap itself!

    name = "z-score", use_raster = FALSE) # higher resolution for output (be careful with PDF output!)
    - -
    The automatically generated colors map from the minus and plus 99^th of the absolute values in the matrix. There are outliers in the matrix whose patterns might be hidden by this
    -color mapping. You can manually set the color to `col` argument.
    +
    +
    The automatically generated colors map from the minus and plus 99^th of
    +the absolute values in the matrix. There are outliers in the matrix
    +whose patterns might be hidden by this color mapping. You can manually
    +set the color to `col` argument.
     
     Use `suppressMessages()` to turn off this message.
    - - + +
    # Shut down current graphics device
     dev.off()
    - -
    null device 
    -          1 
    + +
    png 
    +  2 
    @@ -3473,17 +3592,17 @@

    PCA as an alternative to clustering

    plotPCA() function from DESeq2.

    - +
    # Use plotPCA, but return the data for custom plotting
     pca_df <- plotPCA(vst_data,
                       ntop = 5000, # use the top 5000 genes by variance
                       intgroup = "short_histology",
                       returnData = TRUE)
    - +
    using ntop=5000 top features by variance
    - - + +
    ggplot(pca_df, aes(PC1, PC2, color = short_histology)) +
       geom_point() +
       theme_bw() +
    @@ -3495,8 +3614,8 @@ 

    PCA as an alternative to clustering

    ) + labs(color = "Histology")
    - -

    + +

    @@ -3515,11 +3634,11 @@

    PCA as an alternative to clustering

    Session Info

    - +
    sessionInfo()
    - -
    R version 4.4.0 (2024-04-24)
    +
    +
    R version 4.4.1 (2024-06-14)
     Platform: x86_64-pc-linux-gnu
     Running under: Ubuntu 22.04.4 LTS
     
    @@ -3528,30 +3647,60 @@ 

    Session Info

    LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0 locale: - [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8 LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8 LC_PAPER=C.UTF-8 LC_NAME=C - [9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C + [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C + [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 + [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 + [7] LC_PAPER=en_US.UTF-8 LC_NAME=C + [9] LC_ADDRESS=C LC_TELEPHONE=C +[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C time zone: Etc/UTC tzcode source: system (glibc) attached base packages: -[1] grid stats4 stats graphics grDevices utils datasets methods base +[1] grid stats4 stats graphics grDevices utils datasets +[8] methods base other attached packages: - [1] ComplexHeatmap_2.20.0 lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4 purrr_1.0.2 readr_2.1.5 - [8] tidyr_1.3.1 tibble_3.2.1 ggplot2_3.5.1 tidyverse_2.0.0 DESeq2_1.44.0 SummarizedExperiment_1.34.0 Biobase_2.64.0 -[15] MatrixGenerics_1.16.0 matrixStats_1.3.0 GenomicRanges_1.56.0 GenomeInfoDb_1.40.0 IRanges_2.38.0 S4Vectors_0.42.0 BiocGenerics_0.50.0 + [1] ComplexHeatmap_2.20.0 lubridate_1.9.3 + [3] forcats_1.0.0 stringr_1.5.1 + [5] dplyr_1.1.4 purrr_1.0.2 + [7] readr_2.1.5 tidyr_1.3.1 + [9] tibble_3.2.1 ggplot2_3.5.1 +[11] tidyverse_2.0.0 DESeq2_1.44.0 +[13] SummarizedExperiment_1.34.0 Biobase_2.64.0 +[15] MatrixGenerics_1.16.0 matrixStats_1.3.0 +[17] GenomicRanges_1.56.0 GenomeInfoDb_1.40.0 +[19] IRanges_2.38.0 S4Vectors_0.42.0 +[21] BiocGenerics_0.50.0 optparse_1.7.5 loaded via a namespace (and not attached): - [1] tidyselect_1.2.1 farver_2.1.1 digest_0.6.35 timechange_0.3.0 lifecycle_1.0.4 cluster_2.1.6 Cairo_1.6-2 magrittr_2.0.3 - [9] compiler_4.4.0 rlang_1.1.3 tools_4.4.0 utf8_1.2.4 knitr_1.46 labeling_0.4.3 S4Arrays_1.4.0 bit_4.0.5 -[17] DelayedArray_0.30.0 RColorBrewer_1.1-3 abind_1.4-5 BiocParallel_1.38.0 withr_3.0.0 fansi_1.0.6 colorspace_2.1-0 scales_1.3.0 -[25] iterators_1.0.14 cli_3.6.2 crayon_1.5.2 generics_0.1.3 rstudioapi_0.16.0 httr_1.4.7 tzdb_0.4.0 rjson_0.2.21 -[33] zlibbioc_1.50.0 parallel_4.4.0 XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0 jsonlite_1.8.8 GetoptLong_1.0.5 hms_1.1.3 -[41] bit64_4.0.5 clue_0.3-65 magick_2.8.3 locfit_1.5-9.9 foreach_1.5.2 glue_1.7.0 codetools_0.2-20 stringi_1.8.3 -[49] shape_1.4.6.1 gtable_0.3.5 UCSC.utils_1.0.0 munsell_0.5.1 pillar_1.9.0 GenomeInfoDbData_1.2.12 circlize_0.4.16 R6_2.5.1 -[57] doParallel_1.0.17 vroom_1.6.5 lattice_0.22-6 png_0.1-8 Rcpp_1.0.12 SparseArray_1.4.0 xfun_0.43 fs_1.6.4 -[65] pkgconfig_2.0.3 GlobalOptions_0.1.2
    + [1] tidyselect_1.2.1 farver_2.1.1 fastmap_1.1.1 + [4] digest_0.6.35 timechange_0.3.0 lifecycle_1.0.4 + [7] cluster_2.1.6 Cairo_1.6-2 magrittr_2.0.3 +[10] compiler_4.4.1 rlang_1.1.3 sass_0.4.9 +[13] tools_4.4.1 utf8_1.2.4 yaml_2.3.8 +[16] knitr_1.46 labeling_0.4.3 S4Arrays_1.4.0 +[19] bit_4.0.5 DelayedArray_0.30.0 RColorBrewer_1.1-3 +[22] abind_1.4-5 BiocParallel_1.38.0 withr_3.0.0 +[25] fansi_1.0.6 colorspace_2.1-0 scales_1.3.0 +[28] iterators_1.0.14 cli_3.6.2 rmarkdown_2.26 +[31] crayon_1.5.2 generics_0.1.3 httr_1.4.7 +[34] tzdb_0.4.0 rjson_0.2.21 getopt_1.20.4 +[37] cachem_1.0.8 zlibbioc_1.50.0 parallel_4.4.1 +[40] XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0 +[43] jsonlite_1.8.8 hms_1.1.3 GetoptLong_1.0.5 +[46] bit64_4.0.5 clue_0.3-65 magick_2.8.3 +[49] locfit_1.5-9.9 foreach_1.5.2 jquerylib_0.1.4 +[52] glue_1.7.0 codetools_0.2-20 shape_1.4.6.1 +[55] stringi_1.8.3 gtable_0.3.5 UCSC.utils_1.0.0 +[58] munsell_0.5.1 pillar_1.9.0 htmltools_0.5.8.1 +[61] GenomeInfoDbData_1.2.12 circlize_0.4.16 R6_2.5.1 +[64] doParallel_1.0.17 vroom_1.6.5 evaluate_0.23 +[67] lattice_0.22-6 highr_0.10 png_0.1-8 +[70] bslib_0.7.0 Rcpp_1.0.12 SparseArray_1.4.0 +[73] xfun_0.43 fs_1.6.4 pkgconfig_2.0.3 +[76] GlobalOptions_0.1.2
    diff --git a/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd b/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd index 4d94a5fd..be1e6fa7 100644 --- a/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd +++ b/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd @@ -24,7 +24,7 @@ This notebook will demonstrate how to: #### *More resources for learning R* - [Swirl, an interactive tutorial](https://swirlstats.com/) -- [_R for Data Science_ book](https://r4ds.had.co.nz/) +- [_R for Data Science_ book](https://r4ds.hadley.nz/) - [Tutorial on R, RStudio and R Markdown](https://ismayc.github.io/rbasics-book/) - [Handy R cheatsheets](https://www.posit.co/resources/cheatsheets/) - [R Markdown website](https://rmarkdown.rstudio.com) diff --git a/intro-to-R-tidyverse/01-intro_to_base_R.nb.html b/intro-to-R-tidyverse/01-intro_to_base_R.nb.html index efd95791..ce772647 100644 --- a/intro-to-R-tidyverse/01-intro_to_base_R.nb.html +++ b/intro-to-R-tidyverse/01-intro_to_base_R.nb.html @@ -2910,7 +2910,7 @@

    More resources for learning R

  • Swirl, an interactive tutorial
  • -
  • R for Data Science +
  • R for Data Science book
  • Tutorial on R, @@ -3888,8 +3888,8 @@

    Session Info

    sessionInfo()
    - -
    R version 4.4.0 (2024-04-24)
    +
    +
    R version 4.4.1 (2024-06-14)
     Platform: x86_64-pc-linux-gnu
     Running under: Ubuntu 22.04.4 LTS
     
    @@ -3923,17 +3923,17 @@ 

    Session Info

    [16] evaluate_0.23 jquerylib_0.1.4 tibble_3.2.1 [19] tzdb_0.4.0 fastmap_1.1.1 yaml_2.3.8 [22] lifecycle_1.0.4 palmerpenguins_0.1.1 stringr_1.5.1 -[25] compiler_4.4.0 getopt_1.20.4 pkgconfig_2.0.3 +[25] compiler_4.4.1 getopt_1.20.4 pkgconfig_2.0.3 [28] digest_0.6.35 R6_2.5.1 tidyselect_1.2.1 -[31] utf8_1.2.4 parallel_4.4.0 vroom_1.6.5 +[31] utf8_1.2.4 parallel_4.4.1 vroom_1.6.5 [34] pillar_1.9.0 magrittr_2.0.3 bslib_0.7.0 -[37] bit64_4.0.5 tools_4.4.0 cachem_1.0.8
    +[37] bit64_4.0.5 tools_4.4.1 cachem_1.0.8
    -
    LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgYW5kIFJTdHVkaW8iCmF1dGhvcjogT3JpZ2luYWxseSBhdXRob3JlZCBieSBTdGVwaGFuaWUgSi4gU3BpZWxtYW4sPGJyPmFkYXB0ZWQgYnkgQ0NETCBmb3IgQUxTRgpkYXRlOiAyMDIxCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOiAgCgotIE5hdmlnYXRlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50ICAKLSBVc2UgUiBmb3Igc2ltcGxlIGNhbGN1bGF0aW9ucywgYm90aCBtYXRoZW1hdGljYWwgYW5kIGxvZ2ljYWwgIAotIERlZmluZSBhbmQgdXNlIHZhcmlhYmxlcyBpbiBiYXNlIFIgIAotIFVuZGVyc3RhbmQgYW5kIGFwcGx5IGJhc2UgUiBmdW5jdGlvbnMgICAKLSBVbmRlcnN0YW5kLCBkZWZpbmUsIGFuZCB1c2UgUiBkYXRhIHR5cGVzLCBpbmNsdWRpbmcgdmVjdG9yIG1hbmlwdWxhdGlvbiBhbmQgaW5kZXhpbmcgIAotIFVuZGVyc3RhbmQgdGhlIGFuYXRvbXkgb2YgYSBkYXRhIGZyYW1lICAKCi0tLQoKIyMjIyAqTW9yZSByZXNvdXJjZXMgZm9yIGxlYXJuaW5nIFIqIAoKLSBbU3dpcmwsIGFuIGludGVyYWN0aXZlIHR1dG9yaWFsXShodHRwczovL3N3aXJsc3RhdHMuY29tLykgIAotIFtfUiBmb3IgRGF0YSBTY2llbmNlXyBib29rXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgIAotIFtUdXRvcmlhbCBvbiBSLCBSU3R1ZGlvIGFuZCBSIE1hcmtkb3duXShodHRwczovL2lzbWF5Yy5naXRodWIuaW8vcmJhc2ljcy1ib29rLykgIAotIFtIYW5keSBSIGNoZWF0c2hlZXRzXShodHRwczovL3d3dy5wb3NpdC5jby9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAgCi0gW1IgTWFya2Rvd24gd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pICAKLSBbX1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlX10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgIAoKIyMgV2hhdCBpcyBSPwoKKipSKiogaXMgYSBzdGF0aXN0aWNhbCBjb21wdXRpbmcgbGFuZ3VhZ2UgdGhhdCBpcyBfb3BlbiBzb3VyY2VfLCBtZWFuaW5nIHRoZSB1bmRlcmx5aW5nIGNvZGUgZm9yIHRoZSBsYW5ndWFnZSBpcyBmcmVlbHkgYXZhaWxhYmxlIHRvIGFueW9uZS4gCllvdSBkbyBub3QgbmVlZCBhIHNwZWNpYWwgbGljZW5zZSBvciBzZXQgb2YgcGVybWlzc2lvbnMgdG8gdXNlIGFuZCBkZXZlbG9wIGNvZGUgaW4gUi4gCgpSIGl0c2VsZiBpcyBhbiBfaW50ZXJwcmV0ZWQgY29tcHV0ZXIgbGFuZ3VhZ2VfIGFuZCBjb21lcyB3aXRoIGZ1bmN0aW9uYWxpdHkgdGhhdCBjb21lcyBidW5kbGVkIHdpdGggdGhlIGxhbmd1YWdlIGl0c2VsZiwga25vd24gYXMgKioiYmFzZSBSIioqLgpCdXQgdGhlcmUgaXMgYWxzbyByaWNoIGFkZGl0aW9uYWwgZnVuY3Rpb25hbGl0eSBwcm92aWRlZCBieSAqKmV4dGVybmFsIHBhY2thZ2VzKiosIG9yIGxpYnJhcmllcyBvZiBjb2RlIHRoYXQgYXNzaXN0IGluIGFjY29tcGxpc2hpbmcgY2VydGFpbiB0YXNrcyBhbmQgY2FuIGJlIGZyZWVseSBkb3dubG9hZGVkIGFuZCBsb2FkZWQgZm9yIHVzZS4gCgpJbiB0aGUgbmV4dCBub3RlYm9vayBhbmQgc3Vic2VxdWVudCBtb2R1bGVzLCB3ZSB3aWxsIGJlIHVzaW5nIGEgc3VpdGUgb2YgcGFja2FnZXMgY29sbGVjdGl2ZWx5IGtub3duIGFzIFsqKlRoZSBUaWR5dmVyc2UqKl0oaHR0cHM6Ly90aWR5dmVyc2Uub3JnKS4gClRoZSBgdGlkeXZlcnNlYCBpcyBnZWFyZWQgdG93YXJkcyBpbnR1aXRpdmUgZGF0YSBzY2llbmNlIGFwcGxpY2F0aW9ucyB0aGF0IGZvbGxvdyBhIHNoYXJlZCBkYXRhIHBoaWxvc29waHkuCkJ1dCB0aGVyZSBhcmUgc3RpbGwgbWFueSBjb3JlIGZlYXR1cmVzIG9mIGJhc2UgUiB3aGljaCBhcmUgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mLCBhbmQgd2Ugd2lsbCBiZSB1c2luZyBjb25jZXB0cyBmcm9tIGJvdGggYmFzZSBSIGFuZCB0aGUgdGlkeXZlcnNlIGluIG91ciBhbmFseXNlcywgYXMgd2VsbCBhcyB0YXNrIHNwZWNpZmljIHBhY2thZ2VzIGZvciBhbmFseXNlcyBzdWNoIGFzIGdlbmUgZXhwcmVzc2lvbi4gCgojIyMgV2hhdCBpcyBSU3R1ZGlvPwoKUlN0dWRpbyBpcyBhIF9ncmFwaGljYWwgZW52aXJvbm1lbnRfICgiaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCIgb3IgSURFKSBmb3Igd3JpdGluZyBhbmQgZGV2ZWxvcGluZyBSIGNvZGUuIFJTdHVkaW8gaXMgTk9UIGEgc2VwYXJhdGUgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgLSBpdCBpcyBhbiBpbnRlcmZhY2Ugd2UgdXNlIHRvIGZhY2lsaXRhdGUgUiBwcm9ncmFtbWluZy4gCkluIG90aGVyIHdvcmRzLCB5b3UgY2FuIHByb2dyYW0gaW4gUiB3aXRob3V0IFJTdHVkaW8sIGJ1dCB5b3UgY2FuJ3QgdXNlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50IHdpdGhvdXQgUi4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IFJTdHVkaW8gdGhhbiB5b3UgZXZlciB3YW50ZWQgdG8ga25vdywgc2VlIHRoaXMgW1JTdHVkaW8gSURFIENoZWF0c2hlZXQgKHBkZildKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYWluL3JzdHVkaW8taWRlLnBkZikuCgojIyBUaGUgUlN0dWRpbyBFbnZpcm9ubWVudAoKVGhlIFJTdHVkaW8gZW52aXJvbm1lbnQgaGFzIGZvdXIgbWFpbiAqKnBhbmVzKiosIGVhY2ggb2Ygd2hpY2ggbWF5IGhhdmUgYSBudW1iZXIgb2YgdGFicyB0aGF0IGRpc3BsYXkgZGlmZmVyZW50IGluZm9ybWF0aW9uIG9yIGZ1bmN0aW9uYWxpdHkuICh0aGVpciBzcGVjaWZpYyBsb2NhdGlvbiBjYW4gYmUgY2hhbmdlZCB1bmRlciBUb29scyAtPiBHbG9iYWwgT3B0aW9ucyAtPiBQYW5lIExheW91dCkuCiFbUlN0dWRpbyBBcHBlYXJhbmNlXShzY3JlZW5zaG90cy9yc3R1ZGlvLXBhbmVzLnBuZykgCgoxLiBUaGUgKipFZGl0b3IqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gd3JpdGUgUiBzY3JpcHRzIGFuZCBvdGhlciBkb2N1bWVudHMuIEVhY2ggdGFiIGhlcmUgaXMgaXRzIG93biBkb2N1bWVudC4KVGhpcyBpcyB5b3VyIF90ZXh0IGVkaXRvcl8sIHdoaWNoIHdpbGwgYWxsb3cgeW91IHRvIHNhdmUgeW91ciBSIGNvZGUgZm9yIGZ1dHVyZSB1c2UuIApOb3RlIHRoYXQgY2hhbmdlIGNvZGUgaGVyZSB3aWxsIG5vdCBydW4gYXV0b21hdGljYWxseSB1bnRpbCB5b3UgcnVuIGl0LiAKCjIuIFRoZSAqKkNvbnNvbGUqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gX2ludGVyYWN0aXZlbHlfIHJ1biBSIGNvZGUuIAogICsgVGhlcmUgaXMgYWxzbyBhICoqVGVybWluYWwqKiB0YWIgaGVyZSB3aGljaCBjYW4gYmUgdXNlZCBmb3IgcnVubmluZyBwcm9ncmFtcyBvdXRzaWRlIFIgb24geW91ciBjb21wdXRlcgogIAozLiBUaGUgKipFbnZpcm9ubWVudCoqIHBhbmUgcHJpbWFyaWx5IGRpc3BsYXlzIHRoZSB2YXJpYWJsZXMsIHNvbWV0aW1lcyBrbm93biBhcyBfb2JqZWN0c18gdGhhdCBhcmUgZGVmaW5lZCBkdXJpbmcgYSBnaXZlbiBSIHNlc3Npb24sIGFuZCB3aGF0IGRhdGEgb3IgdmFsdWVzIHRoZXkgbWlnaHQgaG9sZC4KCjQuIFRoZSBmaW5hbCBwYW5lLCAqKkZpbGVzLCBQbG90cywgSGVscCwgLi4uKiosIGhhcyBzZXZlcmFsIHByZXR0eSBpbXBvcnRhbnQgdGFiczoKICAgICsgVGhlICoqRmlsZXMqKiB0YWIgc2hvd3MgdGhlIHN0cnVjdHVyZSBhbmQgY29udGVudHMgb2YgZmlsZXMgYW5kIGZvbGRlcnMgKGFsc28ga25vd24gYXMgZGlyZWN0b3JpZXMpIG9uIHlvdXIgY29tcHV0ZXIuCiAgICArIFRoZSAqKlBsb3RzKiogdGFiIHdpbGwgcmV2ZWFsIHBsb3RzIHdoZW4geW91IG1ha2UgdGhlbQogICAgKyBUaGUgKipQYWNrYWdlcyoqIHRhYiBzaG93cyB3aGljaCBpbnN0YWxsZWQgcGFja2FnZXMgaGF2ZSBiZWVuIGxvYWRlZCBpbnRvIHlvdXIgUiBzZXNzaW9uCiAgICArIFRoZSAqKkhlbHAqKiB0YWIgd2lsbCBzaG93IHRoZSBoZWxwIHBhZ2Ugd2hlbiB5b3UgbG9vayB1cCBhIGZ1bmN0aW9uCiAgICArIFRoZSAqKlZpZXdlcioqIHRhYiB3aWxsIHJldmVhbCBjb21waWxlZCBSIE1hcmtkb3duIGRvY3VtZW50cwoKIyMgQmFzaWMgQ2FsY3VsYXRpb25zCgojIyMgTWF0aGVtYXRpY2FsIG9wZXJhdG9ycwoKVGhlIG1vc3QgYmFzaWMgdXNlIG9mIFIgaXMgYXMgYSByZWd1bGFyIGNhbGN1bGF0b3I6Cgp8IE9wZXJhdGlvbiB8IFN5bWJvbCB8CnwtLS0tLS0tLS0tLXwtLS0tLS0tLXwKfCBBZGQgIHwgYCtgIHwgCnwgU3VidHJhY3QgIHwgYC1gIHwgCnwgTXVsdGlwbHkgIHwgYCpgIHwgCnwgRGl2aWRlICB8IGAvYCB8IAp8IEV4cG9uZW50aWF0ZSB8IGBeYCBvciBgKipgIHwgCgpGb3IgZXhhbXBsZSwgd2UgY2FuIGRvIHNvbWUgc2ltcGxlIG11bHRpcGxpY2F0aW9uIGxpa2UgdGhpcy4gCldoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgCnBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKiBvbiBhIE1hYywgb3IgKkN0cmwrU2hpZnQrRW50ZXIqIG9uIGEgUEMuCgpgYGB7ciBjYWxjdWxhdG9yfQo1ICogNgpgYGAKClVzZSB0aGUgY29uc29sZSB0byBjYWxjdWxhdGUgb3RoZXIgZXhwcmVzc2lvbnMuIFN0YW5kYXJkIG9yZGVyIG9mIG9wZXJhdGlvbnMgYXBwbGllcyAobW9zdGx5KSwgYW5kICB5b3UgY2FuIHVzZSBwYXJlbnRoZXNlcyBgKClgIGFzIHlvdSBtaWdodCBleHBlY3QgKGJ1dCBub3QgYnJhY2tldHMgYFtdYCBvciBicmFjZXNge31gLCB3aGljaCBoYXZlIHNwZWNpYWwgbWVhbmluZ3MpLiBOb3RlIGhvd2V2ZXIsIHRoYXQgeW91IG11c3QgKiphbHdheXMqKiBzcGVjaWZ5IG11bHRpcGxpY2F0aW9uIHdpdGggYCpgOyBpbXBsaWNpdCBtdWx0aXBsaWNhdGlvbiBzdWNoIGFzIGAxMCgzICsgNClgIG9yIGAxMHhgIHdpbGwgbm90IHdvcmsgYW5kIHdpbGwgZ2VuZXJhdGUgYW4gZXJyb3IsIG9yIHdvcnNlLgoKYGBge3IgZXhwcmVzc2lvbnMsIGxpdmUgPSBUUlVFfQoxMCAqICgzICsgNCleMgpgYGAKCgojIyMgRGVmaW5pbmcgYW5kIHVzaW5nIHZhcmlhYmxlcyAKClRvIGRlZmluZSBhIHZhcmlhYmxlLCB3ZSB1c2UgdGhlIF9hc3NpZ25tZW50IG9wZXJhdG9yXyB3aGljaCBsb29rcyBsaWtlIGFuIGFycm93OiBgPC1gLCBmb3IgZXhhbXBsZSBgeCA8LSA3YCB0YWtlcyB0aGUgdmFsdWUgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB0aGUgb3BlcmF0b3IgYW5kIGFzc2lnbnMgaXQgdG8gdGhlIHZhcmlhYmxlIG5hbWUgb24gdGhlIGxlZnQtaGFuZCBzaWRlLiAKCmBgYHtyIHZhci1kZWZpbmUsIGxpdmUgPSBUUlVFfQojIERlZmluZSBhIHZhcmlhYmxlIHggdG8gZXF1YWwgNywgYW5kIHByaW50IG91dCB0aGUgdmFsdWUgb2YgeAp4IDwtIDcKCiMgV2UgY2FuIGhhdmUgUiByZXBlYXQgYmFjayB0byB1cyB3aGF0IGB4YCBpcyBieSBqdXN0IHVzaW5nIGB4YAp4CmBgYAoKU29tZSBmZWF0dXJlcyBvZiB2YXJpYWJsZXMsIGNvbnNpZGVyaW5nIHRoZSBleGFtcGxlIGB4IDwtIDdgOgpFdmVyeSB2YXJpYWJsZSBoYXMgYSAqKm5hbWUqKiwgYSAqKnZhbHVlKiosIGFuZCBhICoqdHlwZSoqLiAKVGhpcyB2YXJpYWJsZSdzIG5hbWUgaXMgYHhgLCBpdHMgdmFsdWUgaXMgYDdgLCBhbmQgaXRzIHR5cGUgaXMgYG51bWVyaWNgICg3IGlzIGEgbnVtYmVyISkuClJlLWRlZmluaW5nIGEgdmFyaWFibGUgd2lsbCBvdmVyd3JpdGUgdGhlIHZhbHVlLgoKYGBge3IgdmFyLXJlZGVmaW5lfQp4IDwtIDUuNQoKeApgYGAKCldlIGNhbiBtb2RpZnkgYW4gZXhpc3RpbmcgdmFyaWFibGUgYnkgcmVhc3NpZ25pbmcgaXQgdG8gaXRzIHNhbWUgbmFtZS4gCkhlcmUgd2UnbGwgYWRkIGAyYCB0byBgeGAgYW5kIHJlYXNzaWduIHRoZSByZXN1bHQgYmFjayB0byBgeGAuIAoKYGBge3IgdmFyLW1vZGlmeSwgbGl2ZSA9IFRSVUV9CnggPC0geCArIDIKCngKYGBgCgojIyMgVmFyaWFibGUgbmFtaW5nIG5vdGU6CkFzIGJlc3QgeW91IGNhbiwgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSB5b3VyIHZhcmlhYmxlIG5hbWVzIGluZm9ybWF0aXZlIChlLmcuIGB4YCBkb2Vzbid0IG1lYW4gYW55dGhpbmcsIGJ1dCBgc2FuZHdpY2hfcHJpY2VgIGlzIG1lYW5pbmdmdWwuLi4gaWYgd2UncmUgdGFsa2luZyBhYm91dCB0aGUgY29zdCBvZiBzYW5kd2ljaGVzLCB0aGF0IGlzLi4pLiAKCiMjIyBDb21tZW50cwoKQXJndWFibHkgdGhlIF9fbW9zdCBpbXBvcnRhbnRfXyBhc3BlY3Qgb2YgeW91ciBjb2RpbmcgaXMgY29tbWVudHM6IFNtYWxsIHBpZWNlcyBvZiBleHBsYW5hdG9yeSB0ZXh0IHlvdSBsZWF2ZSBpbiB5b3VyIGNvZGUgdG8gZXhwbGFpbiB3aGF0IHRoZSBjb2RlIGlzIGRvaW5nIGFuZC9vciBsZWF2ZSBub3RlcyB0byB5b3Vyc2VsZiBvciBvdGhlcnMuIApDb21tZW50cyBhcmUgaW52YWx1YWJsZSBmb3IgY29tbXVuaWNhdGluZyB5b3VyIGNvZGUgdG8gb3RoZXJzLCBidXQgdGhleSBhcmUgbW9zdCBpbXBvcnRhbnQgZm9yICoqRnV0dXJlIFlvdSoqLiAKRnV0dXJlIFlvdSBjb21lcyBpbnRvIGV4aXN0ZW5jZSBhYm91dCBvbmUgc2Vjb25kIGFmdGVyIHlvdSB3cml0ZSBjb2RlLCBhbmQgaGFzIG5vIGlkZWEgd2hhdCBvbiBlYXJ0aCBQYXN0IFlvdSB3YXMgdGhpbmtpbmcuIAoKQ29tbWVudHMgaW4gUiBjb2RlIGFyZSBpbmRpY2F0ZWQgd2l0aCBwb3VuZCBzaWducyAoKmFrYSogaGFzaHRhZ3MsIG9jdG90aG9ycHMpLiBSIHdpbGwgX2lnbm9yZV8gYW55IHRleHQgaW4gYSBsaW5lIGFmdGVyIHRoZSBwb3VuZCBzaWduLCBzbyB5b3UgY2FuIHB1dCB3aGF0ZXZlciB0ZXh0IHlvdSBsaWtlIHRoZXJlLgoKYGBge3IgY29tbWVudHN9CjIyLzcgIyBub3QgcXVpdGUgcGkKCiMgSWYgd2UgbmVlZCBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIHBpLCB3ZSBjYW4gdXNlIEV1bGVyJ3MgZm9ybXVsYQojIFRoaXMgdXNlcyBhdGFuKCksIHdoaWNoIGNhbGN1bGF0ZXMgYXJjdGFuZ2VudC4KMjAgKiBhdGFuKDEvNykgKyA4ICogYXRhbigzLzc5KSAKYGBgCgpIZWxwIG91dCBGdXR1cmUgWW91IGJ5IGFkZGluZyBsb3RzIG9mIGNvbW1lbnRzISAKRnV0dXJlIFlvdSBuZXh0IHdlZWsgdGhpbmtzIFRvZGF5IFlvdSBpcyBhbiBpZGlvdCwgYW5kIHRoZSBvbmx5IHdheSB5b3UgY2FuIGNvbnZpbmNlIEZ1dHVyZSBZb3UgdGhhdCBUb2RheSBZb3UgaXMgcmVhc29uYWJseSBjb21wZXRlbnQgaXMgYnkgYWRkaW5nIGNvbW1lbnRzIGluIHlvdXIgY29kZSBleHBsYWluaW5nIHdoeSBUb2RheSBZb3UgaXMgYWN0dWFsbHkgbm90IHNvIGJhZC4KCiMjIEZ1bmN0aW9ucwpXZSBjYW4gdXNlIHByZS1idWlsdCBjb21wdXRhdGlvbiBtZXRob2RzIGNhbGxlZCAiZnVuY3Rpb25zIiBmb3Igb3RoZXIgb3BlcmF0aW9ucy4gCkZ1bmN0aW9ucyBoYXZlIHRoZSBmb2xsb3dpbmcgZm9ybWF0LCB3aGVyZSB0aGUgX2FyZ3VtZW50XyBpcyB0aGUgaW5mb3JtYXRpb24gd2UgYXJlIHByb3ZpZGluZyB0byB0aGUgZnVuY3Rpb24gZm9yIGl0IHRvIHJ1bi4gCkFuIGV4YW1wbGUgb2YgdGhpcyB3YXMgdGhlIGBhdGFuKClgIGZ1bmN0aW9uIHVzZWQgYWJvdmUuCgpgYGByCmZ1bmN0aW9uX25hbWUoYXJndW1lbnQpCmBgYAoKVG8gbGVhcm4gYWJvdXQgZnVuY3Rpb25zLCB3ZSdsbCBleGFtaW5lIG9uZSBjYWxsZWQgYGxvZygpYCBmaXJzdC4gCgpUbyBrbm93IHdoYXQgYSBmdW5jdGlvbiBkb2VzIGFuZCBob3cgdG8gdXNlIGl0LCB1c2UgdGhlIHF1ZXN0aW9uIG1hcmsgd2hpY2ggd2lsbCByZXZlYWwgZG9jdW1lbnRhdGlvbiBpbiB0aGUgKipoZWxwIHBhbmUqKjogYD9sb2dgCiFbcmhlbHBdKHNjcmVlbnNob3RzL3JoZWxwLWxvZy5wbmcpIAoKVGhlIGRvY3VtZW50YXRpb24gdGVsbHMgdXMgdGhhdCBgbG9nKClgIGlzIGRlcml2ZWQgZnJvbSBge2Jhc2V9YCwgbWVhbmluZyBpdCBpcyBhIGZ1bmN0aW9uIHRoYXQgaXMgcGFydCBvZiBiYXNlIFIuIApJdCBwcm92aWRlcyBhIGJyaWVmIGRlc2NyaXB0aW9uIG9mIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMgYW5kIHNob3dzIHNldmVyYWwgZXhhbXBsZXMgb2YgdG8gaG93IHVzZSBpdC4KCkluIHBhcnRpY3VsYXIsIHRoZSBkb2N1bWVudGF0aW9uIHRlbGxzIHVzIGFib3V0IHdoYXQgYXJndW1lbnQocykgdG8gcHJvdmlkZToKCisgVGhlIGZpcnN0IF9yZXF1aXJlZF8gYXJndW1lbnQgaXMgdGhlIHZhbHVlIHdlJ2QgbGlrZSB0byB0YWtlIHRoZSBsb2cgb2YsIGJ5IGRlZmF1bHQgaXRzIF9uYXR1cmFsIGxvZ18KKyBUaGUgc2Vjb25kIF9vcHRpb25hbF8gYXJndW1lbnQgY2FuIHNwZWNpZnkgYSBkaWZmZXJlbnQgYmFzZSByYXRoZXIgdGhhbiB0aGUgZGVmYXVsdCBgZWAuCgpGdW5jdGlvbnMgYWxzbyBfcmV0dXJuXyB2YWx1ZXMgZm9yIHVzIHRvIHVzZS4gCkluIHRoZSBjYXNlIG9mIGBsb2coKWAsIHRoZSByZXR1cm5lZCB2YWx1ZSBpcyB0aGUgbG9nJ2QgdmFsdWUgdGhlIGZ1bmN0aW9uIGNvbXB1dGVkLgoKYGBge3IgbG9nfQpsb2coNzMpCmBgYAoKSGVyZSB3ZSBjYW4gc3BlY2lmeSBhbiBfYXJndW1lbnRfIG9mIGBiYXNlYCB0byBjYWxjdWxhdGUgbG9nIGJhc2UgMy4gCgpgYGB7ciBsb2czfQpsb2coODEsIGJhc2UgPSAzKQpgYGAKCklmIHdlIGRvbid0IHNwZWNpZnkgdGhlIF9hcmd1bWVudF8gbmFtZXMsIGl0IGFzc3VtZXMgdGhleSBhcmUgaW4gdGhlIG9yZGVyIHRoYXQgYGxvZ2AgZGVmaW5lcyB0aGVtLiAKU2VlIGA/bG9nYCB0byBzZWUgbW9yZSBhYm91dCBpdHMgYXJndW1lbnRzLiAKCmBgYHtyIGxvZzIsIGxpdmUgPSBUUlVFfQpsb2coOCwgMikKYGBgCgpXZSBjYW4gc3dpdGNoIHRoZSBvcmRlciBpZiB3ZSBzcGVjaWZ5IHRoZSBhcmd1bWVudCBuYW1lcy4gCgpgYGB7ciBsb2ctb3JkZXJ9CmxvZyhiYXNlID0gMTAsIHggPSA0MzQyKQpgYGAKCldlIGNhbiBhbHNvIHByb3ZpZGUgdmFyaWFibGVzIGFzIGFyZ3VtZW50cyBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIHJhdyB2YWx1ZXMuIAoKYGBge3IgbG9nLXZhcmlhYmxlfQptZWFuaW5nIDwtIDQyCmxvZyhtZWFuaW5nKQpgYGAKCiMjIFdvcmtpbmcgd2l0aCB2YXJpYWJsZXMKCiMjIyBWYXJpYWJsZSBUeXBlcwoKVmFyaWFibGUgdHlwZXMgaW4gUiBjYW4gc29tZXRpbWVzIGJlIF9jb2VyY2VkXyAoY29udmVydGVkKSBmcm9tIG9uZSB0eXBlIHRvIGFub3RoZXIuCgpgYGB7cn0KIyBEZWZpbmUgYSB2YXJpYWJsZSB3aXRoIGEgbnVtYmVyCnggPC0gMTUKYGBgCgpUaGUgZnVuY3Rpb24gYGNsYXNzKClgIHdpbGwgdGVsbCB1cyB0aGUgdmFyaWFibGUncyB0eXBlLgoKYGBge3J9CmNsYXNzKHgpCmBgYAoKTGV0J3MgY29lcmNlIGl0IHRvIGEgY2hhcmFjdGVyLiAKCmBgYHtyfQp4IDwtIGFzLmNoYXJhY3Rlcih4KQpjbGFzcyh4KQpgYGAKClNlZSBpdCBub3cgaGFzIHF1b3RlcyBhcm91bmQgaXQ/IEl0J3Mgbm93IGEgY2hhcmFjdGVyIGFuZCB3aWxsIGJlaGF2ZSBhcyBzdWNoLgoKYGBge3J9CngKYGBgCgpVc2UgdGhpcyBjaHVuayB0byB0cnkgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgd2l0aCBgeGAsIG5vdyB0aGF0IGl0IGlzIGEgY2hhcmFjdGVyLCB3aGF0IGhhcHBlbnM/IAoKYGBge3IgbGl2ZSA9IFRSVUV9CiMgVHJ5IHRvIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGB4YApgYGAKCkJ1dCB3ZSBjYW4ndCBjb2VyY2UgZXZlcnl0aGluZzoKCmBgYHtyfQojIExldCdzIGNyZWF0ZSBhIGNoYXJhY3RlciB2YXJpYWJsZQp4IDwtICJsb29rIGF0IG15IGNoYXJhY3RlciB2YXJpYWJsZSIKYGBgCgpMZXQncyB0cnkgbWFraW5nIHRoaXMgYSBudW1lcmljIHZhcmlhYmxlOgoKYGBge3IgY29lcmNlLWNoYXIsIGVycm9yPVRSVUV9CnggPC0gYXMubnVtZXJpYyh4KQpgYGAKClByaW50IG91dCBgeGAuCgpgYGB7cn0KeApgYGAKClIgaXMgdGVsbGluZyB1cyBpdCBkb2Vzbid0IGtub3cgaG93IHRvIGNvbnZlcnQgdGhpcyB0byBhIG51bWVyaWMgdmFyaWFibGUsIHNvIGl0IGhhcyByZXR1cm5lZCBgTkFgIGluc3RlYWQuCgpGb3IgcmVmZXJlbmNlLCBoZXJlJ3MgYSBzdW1tYXJ5IG9mIHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlIHR5cGVzLiAKCnwgVmFyaWFibGUgVHlwZSB8IERlZmluaXRpb24gfCBFeGFtcGxlcyB8IENvZXJjaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwgLS0tLS0tLS18CnwgYG51bWVyaWNgICAgICAgIHwgQW55IG51bWJlciB2YWx1ZSB8IGA1YDxicj5gNy41YCA8YnI+YC0xYHwgYGFzLm51bWVyaWMoKWAKfCBgaW50ZWdlcmAgICAgICAgfCBBbnkgX3dob2xlXyBudW1iZXIgdmFsdWUgKG5vIGRlY2ltYWxzKSB8IGA1YCA8YnI+IGAtMTAwYCB8IGBhcy5pbnRlZ2VyKClgCnxgY2hhcmFjdGVyYCAgICAgIHwgQW55IGNvbGxlY3Rpb24gb2YgY2hhcmFjdGVycyBkZWZpbmVkIHdpdGhpbiBfcXVvdGF0aW9uIG1hcmtzXy4gQWxzbyBrbm93biBhcyBhICJzdHJpbmciLiB8IGAiYSJgIChhIHNpbmdsZSBsZXR0ZXIpIDxicj5gInN0cmluZ29mbGV0dGVycyJgIChhIHdob2xlIGJ1bmNoIG9mIGNoYXJhY3RlcnMgcHV0IHRvZ2V0aGVyIGFzIG9uZSkgPGJyPiBgInN0cmluZyBvZiBsZXR0ZXJzIGFuZCBzcGFjZXMiYCA8YnI+IGAiNSJgIDxicj4gYCdzaW5nbGUgcXVvdGVzIGFyZSBhbHNvIGdvb2QnYCB8IGBhcy5jaGFyYWN0ZXIoKWAKfGBsb2dpY2FsYCAgICAgIHwgQSB2YWx1ZSBvZiBgVFJVRWAsIGBGQUxTRWAsIG9yIGBOQWAgfCBgVFJVRWAgPGJyPiBgRkFMU0VgIDxicj4gYE5BYCAobm90IGRlZmluZWQpIHwgYGFzLmxvZ2ljYWwoKWAgCnxgZmFjdG9yYCAgICAgICB8IEEgc3BlY2lhbCB0eXBlIG9mIHZhcmlhYmxlIHRoYXQgZGVub3RlcyBzcGVjaWZpYyBjYXRlZ29yaWVzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgfCAoc3RheSB0dW5lZC4uKSB8IGBhcy5mYWN0b3IoKWAKCiMjIyBWZWN0b3JzCgpZb3Ugd2lsbCBoYXZlIG5vdGljZWQgdGhhdCBhbGwgeW91ciBjb21wdXRhdGlvbnMgdGVuZCB0byBwb3AgdXAgd2l0aCBhIGBbMV1gIHByZWNlZGluZyB0aGVtIGluIFIncyBvdXRwdXQuIApUaGlzIGlzIGJlY2F1c2UsIGluIGZhY3QsIGFsbCAob2sgbW9zdGx5IGFsbCkgdmFyaWFibGVzIGFyZSBfYnkgZGVmYXVsdF8gIHZlY3RvcnMsIGFuZCBvdXIgYW5zd2VycyBhcmUgdGhlIGZpcnN0IChpbiB0aGVzZSBjYXNlcyBvbmx5KSB2YWx1ZSBpbiB0aGUgdmVjdG9yLiAKQXMgdmVjdG9ycyBnZXQgbG9uZ2VyLCBuZXcgaW5kZXggaW5kaWNhdG9ycyB3aWxsIGFwcGVhciBhdCB0aGUgc3RhcnQgb2YgbmV3IGxpbmVzLiAKCmBgYHtyfQojIFRoaXMgaXMgYWN0dWFsbHkgYW4gdmVjdG9yIHRoYXQgaGFzIG9uZSBpdGVtIGluIGl0Lgp4IDwtIDcKYGBgCgpgYGB7ciB2ZWN0b3ItbGVuZ3RofQojIFRoZSBsZW5ndGgoKSBmdW5jdGlvbnMgdGVsbHMgdXMgaG93IGxvbmcgYW4gdmVjdG9yIGlzOgpsZW5ndGgoeCkKYGBgCgpXZSBjYW4gZGVmaW5lIHZlY3RvcnMgd2l0aCB0aGUgZnVuY3Rpb24gYGMoKWAsIHdoaWNoIHN0YW5kcyBmb3IgImNvbWJpbmUiLiAKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIGNvbW1hLXNlcGFyYXRlZCBzZXQgb2YgdmFsdWVzIHRvIHBsYWNlIGluIHRoZSB2ZWN0b3IsIGFuZCByZXR1cm5zIHRoZSB2ZWN0b3IgaXRzZWxmOgoKYGBge3IgbWFrZS12ZWN0b3J9Cm15X251bWVyaWNfdmVjdG9yIDwtIGMoMSwgMSwgMiwgMywgNSwgOCwgMTMsIDIxKQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBidWlsZCBvbiB2ZWN0b3JzIGluIHBsYWNlIGJ5IHJlZGVmaW5pbmcgdGhlbToKCmBgYHtyIGZpYmJvbmFjY2ksIGxpdmUgPSBUUlVFfQojIGFkZCB0aGUgbmV4dCB0d28gRmlib25hY2NpIG51bWJlcnMgdG8gdGhlIHNlcmllcy4KbXlfbnVtZXJpY192ZWN0b3IgPC0gYyhteV9udW1lcmljX3ZlY3RvciwgMzQsIDU1KQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBwdWxsIG91dCBzcGVjaWZpYyBpdGVtcyBmcm9tIGFuIHZlY3RvciB1c2luZyBhIHByb2Nlc3MgY2FsbGVkIF9pbmRleGluZ18sIHdoaWNoIHVzZXMgYnJhY2tldHMgYFtdYCB0byBzcGVjaWZ5IHRoZSBwb3NpdGlvbiBvZiBhbiBpdGVtLiAKCmBgYHtyIHN1YnNldDF9CiMgR3JhYiB0aGUgZm91cnRoIHZhbHVlIGZyb20gbXlfbnVtZXJpY192ZWN0b3IKIyBUaGlzIGdpdmVzIHVzIGFuIHZlY3RvciBvZiBsZW5ndGggMSAKbXlfbnVtZXJpY192ZWN0b3JbNF0KYGBgCgpDb2xvbnMgYXJlIGFsc28gYSBuaWNlIHdheSB0byBxdWlja2x5IG1ha2Ugb3JkZXJlZCBudW1lcmljIHZlY3RvcnMKVXNlIGEgY29sb24gdG8gc3BlY2lmeSBhbiBpbmNsdXNpdmUgcmFuZ2Ugb2YgaW5kaWNlcwpUaGlzIHdpbGwgcmV0dXJuIGFuIHZlY3RvciB3aXRoIDIsIDMsIDQsIGFuZCA1LgoKYGBge3Igc3Vic2V0LW1hbnl9Cm15X251bWVyaWNfdmVjdG9yWzI6NV0KYGBgCgpPbmUgbWFqb3IgYmVuZWZpdCBvZiB2ZWN0b3JzIGlzIHRoZSBjb25jZXB0IG9mICoqdmVjdG9yaXphdGlvbioqLCB3aGVyZSBSIGJ5IGRlZmF1bHQgcGVyZm9ybXMgb3BlcmF0aW9ucyBvbiB0aGUgX2VudGlyZSB2ZWN0b3IgYXQgb25jZV8uIApGb3IgZXhhbXBsZSwgd2UgY2FuIGdldCB0aGUgbG9nIG9mIGFsbCBudW1iZXJzIDEtMjAgd2l0aCBhIHNpbmdsZSwgc2ltcGxlIGNhbGwsIGFuZCBtb3JlIQoKYGBge3IgdmVjdG9yaXplfQp2YWx1ZXNfMV90b18yMCA8LSAxOjIwCmBgYAoKCmBgYHtyIHZlY3Rvcml6ZS1sb2csIGxpdmUgPSBUUlVFfQojIGNhbGN1bGF0ZSB0aGUgbG9nIG9mIHZhbHVlc18xX3RvXzIwCmxvZyh2YWx1ZXNfMV90b18yMCkKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gYXBwbHkgbG9naWNhbCBleHByZXNzaW9ucyB0byB2ZWN0b3JzLCBqdXN0IGFzIHdlIGNhbiBkbyBmb3Igc2luZ2xlIHZhbHVlcy4KVGhlIG91dHB1dCBoZXJlIGlzIGEgbG9naWNhbCB2ZWN0b3IgdGVsbGluZyB1cyB3aGV0aGVyIGVhY2ggdmFsdWUgaW4gZXhhbXBsZV92ZWN0b3IgaXMgVFJVRSBvciBGQUxTRQoKYGBge3IgdmVjdG9yLWNvbXBhcmV9CiMgV2hpY2ggdmFsdWVzIGFyZSA8PSAzPwp2YWx1ZXNfMV90b18yMCA8PSAzCmBgYAoKVGhlcmUgYXJlIHNldmVyYWwga2V5IGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgdXNlZCBvbiB2ZWN0b3JzIGNvbnRhaW5pbmcgbnVtZXJpYyB2YWx1ZXMsIHNvbWUgb2Ygd2hpY2ggYXJlIGJlbG93LgoKKyBgbWVhbigpYDogVGhlIGF2ZXJhZ2UgdmFsdWUgaW4gdGhlIHZlY3RvcgorIGBtaW4oKWA6IFRoZSBtaW5pbXVtIHZhbHVlIGluIHRoZSB2ZWN0b3IKKyBgbWF4KClgOiBUaGUgbWF4aW11bSB2YWx1ZSBpbiB0aGUgdmVjdG9yCisgYHN1bSgpYDogVGhlIHN1bSBvZiBhbGwgdmFsdWVzIGluIHRoZSB2ZWN0b3IKCldlIGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyBvbiB0aGUgdmVjdG9yIGB2YWx1ZXNfMV90b18yMGAgd2UndmUgY3JlYXRlZC4gCgpgYGB7ciB2ZWN0b3ItZnVuY3N9Cm1lYW4odmFsdWVzXzFfdG9fMjApCgojIFRyeSBvdXQgc29tZSBvZiB0aGUgb3RoZXIgZnVuY3Rpb25zIHdlJ3ZlIGxpc3RlZCBhYm92ZSAKCmBgYAoKIyMjIEEgbm90ZSBvbiB2YXJpYWJsZSBuYW1pbmcKCldlIGhhdmUgbGVhcm5lZCBmdW5jdGlvbnMgc3VjaCBhcyBgY2AsIGBsZW5ndGhgLCBgc3VtYCwgYW5kIGV0Yy4gCkltYWdpbmUgZGVmaW5pbmcgYSB2YXJpYWJsZSBjYWxsZWQgYGNgOiBUaGlzIHdpbGwgd29yaywgYnV0IGl0IHdpbGwgbGVhZCB0byBhIApsb3Qgb2YgdW5pbnRlbmRlZCBidWdzLCBzbyBpdCdzIGJlc3QgdG8gYXZvaWQgdGhpcy4gCgojIyMgVGhlIGAlaW4lYCBsb2dpY2FsIG9wZXJhdG9yIAoKYCVpbiVgIGlzIHVzZWZ1bCBmb3IgZGV0ZXJtaW5pbmcgd2hldGhlciBhIGdpdmVuIGl0ZW0ocykgYXJlIGluIGFuIHZlY3Rvci4KCmBgYHtyIGluLW9wZXJhdG9yfQojIGlzIGA3YCBpbiBvdXIgdmVjdG9yPyAKNyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKYGBge3IgaW4yLCBsaXZlID0gVFJVRX0KIyBpcyBgNTBgIGluIG91ciB2ZWN0b3I/IAo1MCAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKV2UgY2FuIHRlc3QgYSB2ZWN0b3Igb2YgdmFsdWVzIGJlaW5nIHdpdGhpbiBhbm90aGVyIHZlY3RvciBvZiB2YWx1ZXMuIAoKYGBge3IgdmVjdG9yLWluLCBsaXZlID0gVFJVRX0KcXVlc3Rpb25fdmFsdWVzIDwtIGMoMTozLCA3LCA1MCkKIyBBcmUgdGhlc2UgdmFsdWVzIGluIG91ciB2ZWN0b3I/CnF1ZXN0aW9uX3ZhbHVlcyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKIyMgRGF0YSBmcmFtZXMKCl9EYXRhIGZyYW1lcyBhcmUgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBmb3IgZGF0YSBhbmFseXNpcyBpbiBSLl8gClRoZXkgYXJlIHRhYmxlcyB3aGljaCBjb25zaXN0IG9mIHJvd3MgYW5kIGNvbHVtbnMsIG11Y2ggbGlrZSBhIF9zcHJlYWRzaGVldF8uIApFYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlIHdoaWNoIGJlaGF2ZXMgYXMgYSBfdmVjdG9yXywgYW5kIGVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uLiAKV2Ugd2lsbCBiZWdpbiBvdXIgZXhwbG9yYXRpb24gd2l0aCBkYXRhc2V0IG9mIG1lYXN1cmVtZW50cyBmcm9tIHRocmVlIHBlbmd1aW4gc3BlY2llcyBtZWFzdXJlZCwgd2hpY2ggd2UgY2FuIGZpbmQgaW4gdGhlIFtgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2VdKGh0dHBzOi8vYWxsaXNvbmhvcnN0LmdpdGh1Yi5pby9wYWxtZXJwZW5ndWlucy8pLiAKV2UnbGwgdGFsayBtb3JlIGFib3V0IHBhY2thZ2VzIHNvb24hClRvIHVzZSB0aGlzIGRhdGFzZXQsIHdlIHdpbGwgbG9hZCBpdCBmcm9tIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UgdXNpbmcgYSBgOjpgIChtb3JlIG9uIHRoaXMgbGF0ZXIpIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lZCBgcGVuZ3VpbnNgIGluIG91ciBjdXJyZW50IGVudmlyb25tZW50LgoKYGBge3IgcGVuZ3Vpbi1saWJyYXJ5fQpwZW5ndWlucyA8LSBwYWxtZXJwZW5ndWluczo6cGVuZ3VpbnMKYGBgCgohW2RyYXdpbmdzIG9mIHBlbmd1aW4gc3BlY2llc10oZGlhZ3JhbXMvbHRlcl9wZW5ndWlucy5wbmcpIEFydHdvcmsgYnkgW0BhbGxpc29uX2hvcnN0XShodHRwczovL3R3aXR0ZXIuY29tL2FsbGlzb25faG9yc3QpCgojIyMgRXhwbG9yaW5nIGRhdGEgZnJhbWVzCgpUaGUgZmlyc3Qgc3RlcCB0byB1c2luZyBhbnkgZGF0YSBpcyB0byBsb29rIGF0IGl0ISEhIApSU3R1ZGlvIGNvbnRhaW5zIGEgc3BlY2lhbCBmdW5jdGlvbiBgVmlldygpYCB3aGljaCBhbGxvd3MgeW91IHRvIGxpdGVyYWxseSB2aWV3IGEgdmFyaWFibGUuCllvdSBjYW4gYWxzbyBjbGljayBvbiB0aGUgb2JqZWN0IGluIHRoZSBlbnZpcm9ubWVudCBwYW5lIHRvIHNlZSBpdHMgb3ZlcmFsbCBwcm9wZXJ0aWVzLCBvciBjbGljayB0aGUgdGFibGUgaWNvbiBvbiB0aGUgb2JqZWN0J3Mgcm93IHRvIGF1dG9tYXRpY2FsbHkgdmlldyB0aGUgdmFyaWFibGUuIAoKU29tZSB1c2VmdWwgZnVuY3Rpb25zIGZvciBleHBsb3Jpbmcgb3VyIGRhdGEgZnJhbWUgaW5jbHVkZToKCisgYGhlYWQoKWAgdG8gc2VlIHRoZSBmaXJzdCA2IHJvd3Mgb2YgYSBkYXRhIGZyYW1lLiBBZGRpdGlvbmFsIGFyZ3VtZW50cyBzdXBwbGllZCBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2Ygcm93cy4KKyBgdGFpbCgpYCB0byBzZWUgdGhlIGxhc3QgNiByb3dzIG9mIGEgZGF0YSBmcmFtZS4gQWRkaXRpb25hbCBhcmd1bWVudHMgc3VwcGxpZWQgY2FuIGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MuCisgYG5hbWVzKClgIHRvIHNlZSB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBkYXRhIGZyYW1lLgorIGBucm93KClgIHRvIHNlZSBob3cgbWFueSByb3dzIGFyZSBpbiB0aGUgZGF0YSBmcmFtZQorIGBuY29sKClgIHRvIHNlZSBob3cgbWFueSBjb2x1bW5zIGFyZSBpbiB0aGUgZGF0YSBmcmFtZS4KCldlIGNhbiBhZGRpdGlvbmFsbHkgZXhwbG9yZSBfb3ZlcmFsbCBwcm9wZXJ0aWVzXyBvZiB0aGUgZGF0YSBmcmFtZSB3aXRoIHR3byBkaWZmZXJlbnQgZnVuY3Rpb25zOiBgc3VtbWFyeSgpYCBhbmQgYHN0cigpYC4KClRoaXMgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGNvbHVtbjoKCmBgYHtyIHBlbmd1aW5zLXN1bW1hcnl9CnN1bW1hcnkocGVuZ3VpbnMpCmBgYAoKVGhpcyBwcm92aWRlcyBhIHNob3J0IHZpZXcgb2YgdGhlICoqc3RyKip1Y3R1cmUgYW5kIGNvbnRlbnRzIG9mIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgcGVuZ3VpbnMtc3RyfQpzdHIocGVuZ3VpbnMpCmBgYAoKWW91J2xsIG5vdGljZSB0aGF0IHRoZSBjb2x1bW4gYHNwZWNpZXNgIGlzIGEgX2ZhY3Rvcl86IFRoaXMgaXMgYSBzcGVjaWFsIHR5cGUgb2YgY2hhcmFjdGVyIHZhcmlhYmxlIHRoYXQgcmVwcmVzZW50cyBkaXN0aW5jdCBjYXRlZ29yaWVzIGtub3duIGFzICJsZXZlbHMiLiAKV2UgaGF2ZSBsZWFybmVkIGhlcmUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgbGV2ZWxzIGluIHRoZSBgc3BlY2llc2AgY29sdW1uOiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvby4KV2UgbWlnaHQgd2FudCB0byBleHBsb3JlIGluZGl2aWR1YWwgY29sdW1ucyBvZiB0aGUgZGF0YSBmcmFtZSBtb3JlIGluLWRlcHRoLiAKV2UgY2FuIGV4YW1pbmUgaW5kaXZpZHVhbCBjb2x1bW5zIHVzaW5nIHRoZSBkb2xsYXIgc2lnbiBgJGAgdG8gc2VsZWN0IG9uZSBieSBuYW1lOgoKYGBge3IgcGVuZ3VpbnMtc3Vic2V0fQojIEV4dHJhY3QgYmlsbF9sZW5ndGhfbW0gYXMgYSB2ZWN0b3IKcGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0KCiMgaW5kZXhpbmcgb3BlcmF0b3JzIGNhbiBiZSB1c2VkIG9uIHRoZXNlIHZlY3RvcnMgdG9vCnBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tWzE6MTBdCmBgYAoKV2UgY2FuIHBlcmZvcm0gb3VyIHJlZ3VsYXIgdmVjdG9yIG9wZXJhdGlvbnMgb24gY29sdW1ucyBkaXJlY3RseS4KCmBgYHtyIHBlbmd1aW5zLWNvbC1tZWFuLCBsaXZlID0gVFJVRX0KIyBjYWxjdWxhdGUgdGhlIG1lYW4gb2YgdGhlIGJpbGxfbGVuZ3RoX21tIGNvbHVtbgptZWFuKHBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tLAogICAgIG5hLnJtID0gVFJVRSkgIyByZW1vdmUgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBtZWFuCmBgYAoKV2UgY2FuIGFsc28gY2FsY3VsYXRlIHRoZSBmdWxsIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBzaW5nbGUgY29sdW1uIGRpcmVjdGx5LiAKCmBgYHtyIHBlbmd1aW5zLWNvbC1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzaG93IGEgc3VtbWFyeSBvZiB0aGUgYmlsbF9sZW5ndGhfbW0gY29sdW1uCnN1bW1hcnkocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0pCmBgYAoKRXh0cmFjdCBgc3BlY2llc2AgYXMgYSB2ZWN0b3IgYW5kIHN1YnNldCBpdCB0byBzZWUgYSBwcmV2aWV3LgoKYGBge3IgcGVuZ3VpbnMtY29sLXN1YnNldCwgbGl2ZSA9IFRSVUV9CiMgZ2V0IHRoZSBmaXJzdCAxMCB2YWx1ZXMgb2YgdGhlIHNwZWNpZXMgY29sdW1uCnBlbmd1aW5zJHNwZWNpZXNbMToxMF0KYGBgCgpBbmQgdmlldyBpdHMgX2xldmVsc18gd2l0aCB0aGUgYGxldmVscygpYCBmdW5jdGlvbi4KCmBgYHtyIHBlbmd1aW4tbGV2ZWxzfQpsZXZlbHMocGVuZ3VpbnMkc3BlY2llcykKYGBgCgojIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCkluIG1hbnkgc2l0dWF0aW9ucywgd2Ugd2lsbCBiZSByZWFkaW5nIGluIHRhYnVsYXIgZGF0YSBmcm9tIGEgZmlsZSBhbmQgdXNpbmcgaXQgYXMgYSBkYXRhIGZyYW1lLiAKVG8gcHJhY3RpY2UsIHdlIHdpbGwgcmVhZCBpbiBhIGZpbGUgd2Ugd2lsbCBiZSB1c2luZyBpbiB0aGUgbmV4dCBub3RlYm9vayBhcyB3ZWxsLCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAsIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gdGhlIGxvY2F0aW9uIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuCgpIZXJlIHdlIHdpbGwgdXNlIGEgZnVuY3Rpb24sIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkJlZm9yZSB3ZSBhcmUgYWJsZSB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBoYXZlIHRvIGxvYWQgdGhlIHBhY2thZ2UgdXNpbmcgYGxpYnJhcnkoKWAuIAoKYGBge3IgcmVhZHJ9CmxpYnJhcnkocmVhZHIpCmBgYAoKYGZpbGUucGF0aCgpYCBjcmVhdGVzIGEgcHJvcGVybHkgZm9ybWF0dGVkIGZpbGUgcGF0aCBieSBhZGRpbmcgYSBwYXRoIHNlcGFyYXRvciAoYC9gIG9uIE1hYyBhbmQgTGludXggb3BlcmF0aW5nIHN5c3RlbXMsIHRoZSBsYXR0ZXIgb2Ygd2hpY2ggaXMgdGhlIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCBvdXIgUlN0dWRpbyBTZXJ2ZXIgcnVucyBvbikgYmV0d2VlbiBzZXBhcmF0ZSBmb2xkZXJzIG9yIGRpcmVjdG9yaWVzLgpCZWNhdXNlIGZpbGUgcGF0aCBzZXBhcmF0b3JzIGNhbiBkaWZmZXIgYmV0d2VlbiB5b3VyIGNvbXB1dGVyIGFuZCB0aGUgY29tcHV0ZXIgb2Ygc29tZW9uZSB3aG8gd2FudHMgdG8gdXNlIHlvdXIgY29kZSwgd2UgdXNlIGBmaWxlLnBhdGgoKWAgaW5zdGVhZCBvZiB0eXBpbmcgb3V0IGAiZGF0YS9nZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2ImAuCkVhY2ggX2FyZ3VtZW50XyB0byBgZmlsZS5wYXRoKClgIGlzIGEgZGlyZWN0b3J5IG9yIGZpbGUgbmFtZS4KWW91J2xsIG5vdGljZSBlYWNoIGFyZ3VtZW50IGlzIGluIHF1b3Rlcywgd2Ugc3BlY2lmeSBgZGF0YWAgZmlyc3QgYmVjYXVzZSB0aGUgZmlsZSwgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGlzIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKCmBgYHtyIGZpbGUucGF0aH0KZmlsZS5wYXRoKCJkYXRhIiwgImdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3YiKQpgYGAKCkFzIHlvdSBjYW4gc2VlIGFib3ZlLCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgYGZpbGUucGF0aCgpYCBpcyB0aGF0IGl0IF9jcmVhdGVzIGEgc3RyaW5nXyB3aXRoIGFuIGFjY3VyYXRlbHktZm9ybWF0dGVkIHBhdGggZm9yIHlvdXIgZmlsZSBzeXN0ZW0uClRoaXMgc3RyaW5nIGNhbiBiZSB1c2VkIG1vdmluZyBmb3J3YXJkIHdoZW4geW91IG5lZWQgdG8gcmVmZXIgdG8gdGhlIHBhdGggdG8geW91ciBmaWxlLgpMZXQncyBnbyBhaGVhZCBhbmQgc3RvcmUgdGhpcyBmaWxlIHBhdGggYXMgYSB2YXJpYWJsZSBpbiBvdXIgZW52aXJvbm1lbnQuIAoKYGBge3IgZmlsZS5wYXRoLXZhcmlhYmxlfQpnZW5lX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIpCmBgYAoKTm93IHdlIGFyZSByZWFkeSB0byB1c2UgYHJlYWRfdHN2KClgIHRvIHJlYWQgdGhlIGZpbGUgaW50byBSLgpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2lsbCBiZSBzdG9yZWQgaW4gYSB2YXJpYWJsZSBuYW1lZCBgc3RhdHNfZGZgLgpOb3RlIHRoZSBgPC1gIChhc3NpZ25tZW50IG9wZXJhdG9yISkgaXMgcmVzcG9uc2libGUgZm9yIHNhdmluZyB0aGlzIHRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuIAoKYGBge3IgcmVhZC1zdGF0c30KIyByZWFkIGluIHRoZSBmaWxlIGBnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2YCBmcm9tIHRoZSBkYXRhIGRpcmVjdG9yeQpzdGF0c19kZiA8LSByZWFkX3RzdihnZW5lX2ZpbGVfcGF0aCkKYGBgCgpUYWtlIGEgbG9vayBhdCB5b3VyIGVudmlyb25tZW50IHBhbmVsIHRvIHNlZSB3aGF0IGBzdGF0c19kZmAgbG9va3MgbGlrZS4gCldlIGNhbiBhbHNvIHByaW50IG91dCBhIHByZXZpZXcgb2YgdGhlIGBzdGF0c19kZmAgZGF0YSBmcmFtZSBoZXJlLiAKCmBgYHtyIHNob3ctc3RhdHMsIGxpdmUgPSBUUlVFfQojIGRpc3BsYXkgc3RhdHNfZGYKc3RhdHNfZGYKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpBdCB0aGUgZW5kIG9mIGV2ZXJ5IG5vdGVib29rLCB5b3Ugd2lsbCBzZWUgdXMgcHJpbnQgb3V0IGBzZXNzaW9uSW5mb2AuIApUaGlzIGFpZHMgaW4gdGhlIHJlcHJvZHVjaWJpbGl0eSBvZiB5b3VyIGNvZGUgYnkgc2hvd2luZyBleGFjdGx5IHdoYXQgcGFja2FnZXMgCmFuZCB2ZXJzaW9ucyB3ZXJlIGJlaW5nIHVzZWQgdGhlIGxhc3QgdGltZSB0aGUgbm90ZWJvb2sgd2FzIHJ1bi4KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=
    +
    LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgYW5kIFJTdHVkaW8iCmF1dGhvcjogT3JpZ2luYWxseSBhdXRob3JlZCBieSBTdGVwaGFuaWUgSi4gU3BpZWxtYW4sPGJyPmFkYXB0ZWQgYnkgQ0NETCBmb3IgQUxTRgpkYXRlOiAyMDIxCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOiAgCgotIE5hdmlnYXRlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50ICAKLSBVc2UgUiBmb3Igc2ltcGxlIGNhbGN1bGF0aW9ucywgYm90aCBtYXRoZW1hdGljYWwgYW5kIGxvZ2ljYWwgIAotIERlZmluZSBhbmQgdXNlIHZhcmlhYmxlcyBpbiBiYXNlIFIgIAotIFVuZGVyc3RhbmQgYW5kIGFwcGx5IGJhc2UgUiBmdW5jdGlvbnMgICAKLSBVbmRlcnN0YW5kLCBkZWZpbmUsIGFuZCB1c2UgUiBkYXRhIHR5cGVzLCBpbmNsdWRpbmcgdmVjdG9yIG1hbmlwdWxhdGlvbiBhbmQgaW5kZXhpbmcgIAotIFVuZGVyc3RhbmQgdGhlIGFuYXRvbXkgb2YgYSBkYXRhIGZyYW1lICAKCi0tLQoKIyMjIyAqTW9yZSByZXNvdXJjZXMgZm9yIGxlYXJuaW5nIFIqIAoKLSBbU3dpcmwsIGFuIGludGVyYWN0aXZlIHR1dG9yaWFsXShodHRwczovL3N3aXJsc3RhdHMuY29tLykgIAotIFtfUiBmb3IgRGF0YSBTY2llbmNlXyBib29rXShodHRwczovL3I0ZHMuaGFkbGV5Lm56LykgIAotIFtUdXRvcmlhbCBvbiBSLCBSU3R1ZGlvIGFuZCBSIE1hcmtkb3duXShodHRwczovL2lzbWF5Yy5naXRodWIuaW8vcmJhc2ljcy1ib29rLykgIAotIFtIYW5keSBSIGNoZWF0c2hlZXRzXShodHRwczovL3d3dy5wb3NpdC5jby9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAgCi0gW1IgTWFya2Rvd24gd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pICAKLSBbX1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlX10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgIAoKIyMgV2hhdCBpcyBSPwoKKipSKiogaXMgYSBzdGF0aXN0aWNhbCBjb21wdXRpbmcgbGFuZ3VhZ2UgdGhhdCBpcyBfb3BlbiBzb3VyY2VfLCBtZWFuaW5nIHRoZSB1bmRlcmx5aW5nIGNvZGUgZm9yIHRoZSBsYW5ndWFnZSBpcyBmcmVlbHkgYXZhaWxhYmxlIHRvIGFueW9uZS4gCllvdSBkbyBub3QgbmVlZCBhIHNwZWNpYWwgbGljZW5zZSBvciBzZXQgb2YgcGVybWlzc2lvbnMgdG8gdXNlIGFuZCBkZXZlbG9wIGNvZGUgaW4gUi4gCgpSIGl0c2VsZiBpcyBhbiBfaW50ZXJwcmV0ZWQgY29tcHV0ZXIgbGFuZ3VhZ2VfIGFuZCBjb21lcyB3aXRoIGZ1bmN0aW9uYWxpdHkgdGhhdCBjb21lcyBidW5kbGVkIHdpdGggdGhlIGxhbmd1YWdlIGl0c2VsZiwga25vd24gYXMgKioiYmFzZSBSIioqLgpCdXQgdGhlcmUgaXMgYWxzbyByaWNoIGFkZGl0aW9uYWwgZnVuY3Rpb25hbGl0eSBwcm92aWRlZCBieSAqKmV4dGVybmFsIHBhY2thZ2VzKiosIG9yIGxpYnJhcmllcyBvZiBjb2RlIHRoYXQgYXNzaXN0IGluIGFjY29tcGxpc2hpbmcgY2VydGFpbiB0YXNrcyBhbmQgY2FuIGJlIGZyZWVseSBkb3dubG9hZGVkIGFuZCBsb2FkZWQgZm9yIHVzZS4gCgpJbiB0aGUgbmV4dCBub3RlYm9vayBhbmQgc3Vic2VxdWVudCBtb2R1bGVzLCB3ZSB3aWxsIGJlIHVzaW5nIGEgc3VpdGUgb2YgcGFja2FnZXMgY29sbGVjdGl2ZWx5IGtub3duIGFzIFsqKlRoZSBUaWR5dmVyc2UqKl0oaHR0cHM6Ly90aWR5dmVyc2Uub3JnKS4gClRoZSBgdGlkeXZlcnNlYCBpcyBnZWFyZWQgdG93YXJkcyBpbnR1aXRpdmUgZGF0YSBzY2llbmNlIGFwcGxpY2F0aW9ucyB0aGF0IGZvbGxvdyBhIHNoYXJlZCBkYXRhIHBoaWxvc29waHkuCkJ1dCB0aGVyZSBhcmUgc3RpbGwgbWFueSBjb3JlIGZlYXR1cmVzIG9mIGJhc2UgUiB3aGljaCBhcmUgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mLCBhbmQgd2Ugd2lsbCBiZSB1c2luZyBjb25jZXB0cyBmcm9tIGJvdGggYmFzZSBSIGFuZCB0aGUgdGlkeXZlcnNlIGluIG91ciBhbmFseXNlcywgYXMgd2VsbCBhcyB0YXNrIHNwZWNpZmljIHBhY2thZ2VzIGZvciBhbmFseXNlcyBzdWNoIGFzIGdlbmUgZXhwcmVzc2lvbi4gCgojIyMgV2hhdCBpcyBSU3R1ZGlvPwoKUlN0dWRpbyBpcyBhIF9ncmFwaGljYWwgZW52aXJvbm1lbnRfICgiaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCIgb3IgSURFKSBmb3Igd3JpdGluZyBhbmQgZGV2ZWxvcGluZyBSIGNvZGUuIFJTdHVkaW8gaXMgTk9UIGEgc2VwYXJhdGUgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgLSBpdCBpcyBhbiBpbnRlcmZhY2Ugd2UgdXNlIHRvIGZhY2lsaXRhdGUgUiBwcm9ncmFtbWluZy4gCkluIG90aGVyIHdvcmRzLCB5b3UgY2FuIHByb2dyYW0gaW4gUiB3aXRob3V0IFJTdHVkaW8sIGJ1dCB5b3UgY2FuJ3QgdXNlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50IHdpdGhvdXQgUi4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IFJTdHVkaW8gdGhhbiB5b3UgZXZlciB3YW50ZWQgdG8ga25vdywgc2VlIHRoaXMgW1JTdHVkaW8gSURFIENoZWF0c2hlZXQgKHBkZildKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYWluL3JzdHVkaW8taWRlLnBkZikuCgojIyBUaGUgUlN0dWRpbyBFbnZpcm9ubWVudAoKVGhlIFJTdHVkaW8gZW52aXJvbm1lbnQgaGFzIGZvdXIgbWFpbiAqKnBhbmVzKiosIGVhY2ggb2Ygd2hpY2ggbWF5IGhhdmUgYSBudW1iZXIgb2YgdGFicyB0aGF0IGRpc3BsYXkgZGlmZmVyZW50IGluZm9ybWF0aW9uIG9yIGZ1bmN0aW9uYWxpdHkuICh0aGVpciBzcGVjaWZpYyBsb2NhdGlvbiBjYW4gYmUgY2hhbmdlZCB1bmRlciBUb29scyAtPiBHbG9iYWwgT3B0aW9ucyAtPiBQYW5lIExheW91dCkuCiFbUlN0dWRpbyBBcHBlYXJhbmNlXShzY3JlZW5zaG90cy9yc3R1ZGlvLXBhbmVzLnBuZykgCgoxLiBUaGUgKipFZGl0b3IqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gd3JpdGUgUiBzY3JpcHRzIGFuZCBvdGhlciBkb2N1bWVudHMuIEVhY2ggdGFiIGhlcmUgaXMgaXRzIG93biBkb2N1bWVudC4KVGhpcyBpcyB5b3VyIF90ZXh0IGVkaXRvcl8sIHdoaWNoIHdpbGwgYWxsb3cgeW91IHRvIHNhdmUgeW91ciBSIGNvZGUgZm9yIGZ1dHVyZSB1c2UuIApOb3RlIHRoYXQgY2hhbmdlIGNvZGUgaGVyZSB3aWxsIG5vdCBydW4gYXV0b21hdGljYWxseSB1bnRpbCB5b3UgcnVuIGl0LiAKCjIuIFRoZSAqKkNvbnNvbGUqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gX2ludGVyYWN0aXZlbHlfIHJ1biBSIGNvZGUuIAogICsgVGhlcmUgaXMgYWxzbyBhICoqVGVybWluYWwqKiB0YWIgaGVyZSB3aGljaCBjYW4gYmUgdXNlZCBmb3IgcnVubmluZyBwcm9ncmFtcyBvdXRzaWRlIFIgb24geW91ciBjb21wdXRlcgogIAozLiBUaGUgKipFbnZpcm9ubWVudCoqIHBhbmUgcHJpbWFyaWx5IGRpc3BsYXlzIHRoZSB2YXJpYWJsZXMsIHNvbWV0aW1lcyBrbm93biBhcyBfb2JqZWN0c18gdGhhdCBhcmUgZGVmaW5lZCBkdXJpbmcgYSBnaXZlbiBSIHNlc3Npb24sIGFuZCB3aGF0IGRhdGEgb3IgdmFsdWVzIHRoZXkgbWlnaHQgaG9sZC4KCjQuIFRoZSBmaW5hbCBwYW5lLCAqKkZpbGVzLCBQbG90cywgSGVscCwgLi4uKiosIGhhcyBzZXZlcmFsIHByZXR0eSBpbXBvcnRhbnQgdGFiczoKICAgICsgVGhlICoqRmlsZXMqKiB0YWIgc2hvd3MgdGhlIHN0cnVjdHVyZSBhbmQgY29udGVudHMgb2YgZmlsZXMgYW5kIGZvbGRlcnMgKGFsc28ga25vd24gYXMgZGlyZWN0b3JpZXMpIG9uIHlvdXIgY29tcHV0ZXIuCiAgICArIFRoZSAqKlBsb3RzKiogdGFiIHdpbGwgcmV2ZWFsIHBsb3RzIHdoZW4geW91IG1ha2UgdGhlbQogICAgKyBUaGUgKipQYWNrYWdlcyoqIHRhYiBzaG93cyB3aGljaCBpbnN0YWxsZWQgcGFja2FnZXMgaGF2ZSBiZWVuIGxvYWRlZCBpbnRvIHlvdXIgUiBzZXNzaW9uCiAgICArIFRoZSAqKkhlbHAqKiB0YWIgd2lsbCBzaG93IHRoZSBoZWxwIHBhZ2Ugd2hlbiB5b3UgbG9vayB1cCBhIGZ1bmN0aW9uCiAgICArIFRoZSAqKlZpZXdlcioqIHRhYiB3aWxsIHJldmVhbCBjb21waWxlZCBSIE1hcmtkb3duIGRvY3VtZW50cwoKIyMgQmFzaWMgQ2FsY3VsYXRpb25zCgojIyMgTWF0aGVtYXRpY2FsIG9wZXJhdG9ycwoKVGhlIG1vc3QgYmFzaWMgdXNlIG9mIFIgaXMgYXMgYSByZWd1bGFyIGNhbGN1bGF0b3I6Cgp8IE9wZXJhdGlvbiB8IFN5bWJvbCB8CnwtLS0tLS0tLS0tLXwtLS0tLS0tLXwKfCBBZGQgIHwgYCtgIHwgCnwgU3VidHJhY3QgIHwgYC1gIHwgCnwgTXVsdGlwbHkgIHwgYCpgIHwgCnwgRGl2aWRlICB8IGAvYCB8IAp8IEV4cG9uZW50aWF0ZSB8IGBeYCBvciBgKipgIHwgCgpGb3IgZXhhbXBsZSwgd2UgY2FuIGRvIHNvbWUgc2ltcGxlIG11bHRpcGxpY2F0aW9uIGxpa2UgdGhpcy4gCldoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgCnBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKiBvbiBhIE1hYywgb3IgKkN0cmwrU2hpZnQrRW50ZXIqIG9uIGEgUEMuCgpgYGB7ciBjYWxjdWxhdG9yfQo1ICogNgpgYGAKClVzZSB0aGUgY29uc29sZSB0byBjYWxjdWxhdGUgb3RoZXIgZXhwcmVzc2lvbnMuIFN0YW5kYXJkIG9yZGVyIG9mIG9wZXJhdGlvbnMgYXBwbGllcyAobW9zdGx5KSwgYW5kICB5b3UgY2FuIHVzZSBwYXJlbnRoZXNlcyBgKClgIGFzIHlvdSBtaWdodCBleHBlY3QgKGJ1dCBub3QgYnJhY2tldHMgYFtdYCBvciBicmFjZXNge31gLCB3aGljaCBoYXZlIHNwZWNpYWwgbWVhbmluZ3MpLiBOb3RlIGhvd2V2ZXIsIHRoYXQgeW91IG11c3QgKiphbHdheXMqKiBzcGVjaWZ5IG11bHRpcGxpY2F0aW9uIHdpdGggYCpgOyBpbXBsaWNpdCBtdWx0aXBsaWNhdGlvbiBzdWNoIGFzIGAxMCgzICsgNClgIG9yIGAxMHhgIHdpbGwgbm90IHdvcmsgYW5kIHdpbGwgZ2VuZXJhdGUgYW4gZXJyb3IsIG9yIHdvcnNlLgoKYGBge3IgZXhwcmVzc2lvbnMsIGxpdmUgPSBUUlVFfQoxMCAqICgzICsgNCleMgpgYGAKCgojIyMgRGVmaW5pbmcgYW5kIHVzaW5nIHZhcmlhYmxlcyAKClRvIGRlZmluZSBhIHZhcmlhYmxlLCB3ZSB1c2UgdGhlIF9hc3NpZ25tZW50IG9wZXJhdG9yXyB3aGljaCBsb29rcyBsaWtlIGFuIGFycm93OiBgPC1gLCBmb3IgZXhhbXBsZSBgeCA8LSA3YCB0YWtlcyB0aGUgdmFsdWUgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB0aGUgb3BlcmF0b3IgYW5kIGFzc2lnbnMgaXQgdG8gdGhlIHZhcmlhYmxlIG5hbWUgb24gdGhlIGxlZnQtaGFuZCBzaWRlLiAKCmBgYHtyIHZhci1kZWZpbmUsIGxpdmUgPSBUUlVFfQojIERlZmluZSBhIHZhcmlhYmxlIHggdG8gZXF1YWwgNywgYW5kIHByaW50IG91dCB0aGUgdmFsdWUgb2YgeAp4IDwtIDcKCiMgV2UgY2FuIGhhdmUgUiByZXBlYXQgYmFjayB0byB1cyB3aGF0IGB4YCBpcyBieSBqdXN0IHVzaW5nIGB4YAp4CmBgYAoKU29tZSBmZWF0dXJlcyBvZiB2YXJpYWJsZXMsIGNvbnNpZGVyaW5nIHRoZSBleGFtcGxlIGB4IDwtIDdgOgpFdmVyeSB2YXJpYWJsZSBoYXMgYSAqKm5hbWUqKiwgYSAqKnZhbHVlKiosIGFuZCBhICoqdHlwZSoqLiAKVGhpcyB2YXJpYWJsZSdzIG5hbWUgaXMgYHhgLCBpdHMgdmFsdWUgaXMgYDdgLCBhbmQgaXRzIHR5cGUgaXMgYG51bWVyaWNgICg3IGlzIGEgbnVtYmVyISkuClJlLWRlZmluaW5nIGEgdmFyaWFibGUgd2lsbCBvdmVyd3JpdGUgdGhlIHZhbHVlLgoKYGBge3IgdmFyLXJlZGVmaW5lfQp4IDwtIDUuNQoKeApgYGAKCldlIGNhbiBtb2RpZnkgYW4gZXhpc3RpbmcgdmFyaWFibGUgYnkgcmVhc3NpZ25pbmcgaXQgdG8gaXRzIHNhbWUgbmFtZS4gCkhlcmUgd2UnbGwgYWRkIGAyYCB0byBgeGAgYW5kIHJlYXNzaWduIHRoZSByZXN1bHQgYmFjayB0byBgeGAuIAoKYGBge3IgdmFyLW1vZGlmeSwgbGl2ZSA9IFRSVUV9CnggPC0geCArIDIKCngKYGBgCgojIyMgVmFyaWFibGUgbmFtaW5nIG5vdGU6CkFzIGJlc3QgeW91IGNhbiwgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSB5b3VyIHZhcmlhYmxlIG5hbWVzIGluZm9ybWF0aXZlIChlLmcuIGB4YCBkb2Vzbid0IG1lYW4gYW55dGhpbmcsIGJ1dCBgc2FuZHdpY2hfcHJpY2VgIGlzIG1lYW5pbmdmdWwuLi4gaWYgd2UncmUgdGFsa2luZyBhYm91dCB0aGUgY29zdCBvZiBzYW5kd2ljaGVzLCB0aGF0IGlzLi4pLiAKCiMjIyBDb21tZW50cwoKQXJndWFibHkgdGhlIF9fbW9zdCBpbXBvcnRhbnRfXyBhc3BlY3Qgb2YgeW91ciBjb2RpbmcgaXMgY29tbWVudHM6IFNtYWxsIHBpZWNlcyBvZiBleHBsYW5hdG9yeSB0ZXh0IHlvdSBsZWF2ZSBpbiB5b3VyIGNvZGUgdG8gZXhwbGFpbiB3aGF0IHRoZSBjb2RlIGlzIGRvaW5nIGFuZC9vciBsZWF2ZSBub3RlcyB0byB5b3Vyc2VsZiBvciBvdGhlcnMuIApDb21tZW50cyBhcmUgaW52YWx1YWJsZSBmb3IgY29tbXVuaWNhdGluZyB5b3VyIGNvZGUgdG8gb3RoZXJzLCBidXQgdGhleSBhcmUgbW9zdCBpbXBvcnRhbnQgZm9yICoqRnV0dXJlIFlvdSoqLiAKRnV0dXJlIFlvdSBjb21lcyBpbnRvIGV4aXN0ZW5jZSBhYm91dCBvbmUgc2Vjb25kIGFmdGVyIHlvdSB3cml0ZSBjb2RlLCBhbmQgaGFzIG5vIGlkZWEgd2hhdCBvbiBlYXJ0aCBQYXN0IFlvdSB3YXMgdGhpbmtpbmcuIAoKQ29tbWVudHMgaW4gUiBjb2RlIGFyZSBpbmRpY2F0ZWQgd2l0aCBwb3VuZCBzaWducyAoKmFrYSogaGFzaHRhZ3MsIG9jdG90aG9ycHMpLiBSIHdpbGwgX2lnbm9yZV8gYW55IHRleHQgaW4gYSBsaW5lIGFmdGVyIHRoZSBwb3VuZCBzaWduLCBzbyB5b3UgY2FuIHB1dCB3aGF0ZXZlciB0ZXh0IHlvdSBsaWtlIHRoZXJlLgoKYGBge3IgY29tbWVudHN9CjIyLzcgIyBub3QgcXVpdGUgcGkKCiMgSWYgd2UgbmVlZCBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIHBpLCB3ZSBjYW4gdXNlIEV1bGVyJ3MgZm9ybXVsYQojIFRoaXMgdXNlcyBhdGFuKCksIHdoaWNoIGNhbGN1bGF0ZXMgYXJjdGFuZ2VudC4KMjAgKiBhdGFuKDEvNykgKyA4ICogYXRhbigzLzc5KSAKYGBgCgpIZWxwIG91dCBGdXR1cmUgWW91IGJ5IGFkZGluZyBsb3RzIG9mIGNvbW1lbnRzISAKRnV0dXJlIFlvdSBuZXh0IHdlZWsgdGhpbmtzIFRvZGF5IFlvdSBpcyBhbiBpZGlvdCwgYW5kIHRoZSBvbmx5IHdheSB5b3UgY2FuIGNvbnZpbmNlIEZ1dHVyZSBZb3UgdGhhdCBUb2RheSBZb3UgaXMgcmVhc29uYWJseSBjb21wZXRlbnQgaXMgYnkgYWRkaW5nIGNvbW1lbnRzIGluIHlvdXIgY29kZSBleHBsYWluaW5nIHdoeSBUb2RheSBZb3UgaXMgYWN0dWFsbHkgbm90IHNvIGJhZC4KCiMjIEZ1bmN0aW9ucwpXZSBjYW4gdXNlIHByZS1idWlsdCBjb21wdXRhdGlvbiBtZXRob2RzIGNhbGxlZCAiZnVuY3Rpb25zIiBmb3Igb3RoZXIgb3BlcmF0aW9ucy4gCkZ1bmN0aW9ucyBoYXZlIHRoZSBmb2xsb3dpbmcgZm9ybWF0LCB3aGVyZSB0aGUgX2FyZ3VtZW50XyBpcyB0aGUgaW5mb3JtYXRpb24gd2UgYXJlIHByb3ZpZGluZyB0byB0aGUgZnVuY3Rpb24gZm9yIGl0IHRvIHJ1bi4gCkFuIGV4YW1wbGUgb2YgdGhpcyB3YXMgdGhlIGBhdGFuKClgIGZ1bmN0aW9uIHVzZWQgYWJvdmUuCgpgYGByCmZ1bmN0aW9uX25hbWUoYXJndW1lbnQpCmBgYAoKVG8gbGVhcm4gYWJvdXQgZnVuY3Rpb25zLCB3ZSdsbCBleGFtaW5lIG9uZSBjYWxsZWQgYGxvZygpYCBmaXJzdC4gCgpUbyBrbm93IHdoYXQgYSBmdW5jdGlvbiBkb2VzIGFuZCBob3cgdG8gdXNlIGl0LCB1c2UgdGhlIHF1ZXN0aW9uIG1hcmsgd2hpY2ggd2lsbCByZXZlYWwgZG9jdW1lbnRhdGlvbiBpbiB0aGUgKipoZWxwIHBhbmUqKjogYD9sb2dgCiFbcmhlbHBdKHNjcmVlbnNob3RzL3JoZWxwLWxvZy5wbmcpIAoKVGhlIGRvY3VtZW50YXRpb24gdGVsbHMgdXMgdGhhdCBgbG9nKClgIGlzIGRlcml2ZWQgZnJvbSBge2Jhc2V9YCwgbWVhbmluZyBpdCBpcyBhIGZ1bmN0aW9uIHRoYXQgaXMgcGFydCBvZiBiYXNlIFIuIApJdCBwcm92aWRlcyBhIGJyaWVmIGRlc2NyaXB0aW9uIG9mIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMgYW5kIHNob3dzIHNldmVyYWwgZXhhbXBsZXMgb2YgdG8gaG93IHVzZSBpdC4KCkluIHBhcnRpY3VsYXIsIHRoZSBkb2N1bWVudGF0aW9uIHRlbGxzIHVzIGFib3V0IHdoYXQgYXJndW1lbnQocykgdG8gcHJvdmlkZToKCisgVGhlIGZpcnN0IF9yZXF1aXJlZF8gYXJndW1lbnQgaXMgdGhlIHZhbHVlIHdlJ2QgbGlrZSB0byB0YWtlIHRoZSBsb2cgb2YsIGJ5IGRlZmF1bHQgaXRzIF9uYXR1cmFsIGxvZ18KKyBUaGUgc2Vjb25kIF9vcHRpb25hbF8gYXJndW1lbnQgY2FuIHNwZWNpZnkgYSBkaWZmZXJlbnQgYmFzZSByYXRoZXIgdGhhbiB0aGUgZGVmYXVsdCBgZWAuCgpGdW5jdGlvbnMgYWxzbyBfcmV0dXJuXyB2YWx1ZXMgZm9yIHVzIHRvIHVzZS4gCkluIHRoZSBjYXNlIG9mIGBsb2coKWAsIHRoZSByZXR1cm5lZCB2YWx1ZSBpcyB0aGUgbG9nJ2QgdmFsdWUgdGhlIGZ1bmN0aW9uIGNvbXB1dGVkLgoKYGBge3IgbG9nfQpsb2coNzMpCmBgYAoKSGVyZSB3ZSBjYW4gc3BlY2lmeSBhbiBfYXJndW1lbnRfIG9mIGBiYXNlYCB0byBjYWxjdWxhdGUgbG9nIGJhc2UgMy4gCgpgYGB7ciBsb2czfQpsb2coODEsIGJhc2UgPSAzKQpgYGAKCklmIHdlIGRvbid0IHNwZWNpZnkgdGhlIF9hcmd1bWVudF8gbmFtZXMsIGl0IGFzc3VtZXMgdGhleSBhcmUgaW4gdGhlIG9yZGVyIHRoYXQgYGxvZ2AgZGVmaW5lcyB0aGVtLiAKU2VlIGA/bG9nYCB0byBzZWUgbW9yZSBhYm91dCBpdHMgYXJndW1lbnRzLiAKCmBgYHtyIGxvZzIsIGxpdmUgPSBUUlVFfQpsb2coOCwgMikKYGBgCgpXZSBjYW4gc3dpdGNoIHRoZSBvcmRlciBpZiB3ZSBzcGVjaWZ5IHRoZSBhcmd1bWVudCBuYW1lcy4gCgpgYGB7ciBsb2ctb3JkZXJ9CmxvZyhiYXNlID0gMTAsIHggPSA0MzQyKQpgYGAKCldlIGNhbiBhbHNvIHByb3ZpZGUgdmFyaWFibGVzIGFzIGFyZ3VtZW50cyBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIHJhdyB2YWx1ZXMuIAoKYGBge3IgbG9nLXZhcmlhYmxlfQptZWFuaW5nIDwtIDQyCmxvZyhtZWFuaW5nKQpgYGAKCiMjIFdvcmtpbmcgd2l0aCB2YXJpYWJsZXMKCiMjIyBWYXJpYWJsZSBUeXBlcwoKVmFyaWFibGUgdHlwZXMgaW4gUiBjYW4gc29tZXRpbWVzIGJlIF9jb2VyY2VkXyAoY29udmVydGVkKSBmcm9tIG9uZSB0eXBlIHRvIGFub3RoZXIuCgpgYGB7cn0KIyBEZWZpbmUgYSB2YXJpYWJsZSB3aXRoIGEgbnVtYmVyCnggPC0gMTUKYGBgCgpUaGUgZnVuY3Rpb24gYGNsYXNzKClgIHdpbGwgdGVsbCB1cyB0aGUgdmFyaWFibGUncyB0eXBlLgoKYGBge3J9CmNsYXNzKHgpCmBgYAoKTGV0J3MgY29lcmNlIGl0IHRvIGEgY2hhcmFjdGVyLiAKCmBgYHtyfQp4IDwtIGFzLmNoYXJhY3Rlcih4KQpjbGFzcyh4KQpgYGAKClNlZSBpdCBub3cgaGFzIHF1b3RlcyBhcm91bmQgaXQ/IEl0J3Mgbm93IGEgY2hhcmFjdGVyIGFuZCB3aWxsIGJlaGF2ZSBhcyBzdWNoLgoKYGBge3J9CngKYGBgCgpVc2UgdGhpcyBjaHVuayB0byB0cnkgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgd2l0aCBgeGAsIG5vdyB0aGF0IGl0IGlzIGEgY2hhcmFjdGVyLCB3aGF0IGhhcHBlbnM/IAoKYGBge3IgbGl2ZSA9IFRSVUV9CiMgVHJ5IHRvIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGB4YApgYGAKCkJ1dCB3ZSBjYW4ndCBjb2VyY2UgZXZlcnl0aGluZzoKCmBgYHtyfQojIExldCdzIGNyZWF0ZSBhIGNoYXJhY3RlciB2YXJpYWJsZQp4IDwtICJsb29rIGF0IG15IGNoYXJhY3RlciB2YXJpYWJsZSIKYGBgCgpMZXQncyB0cnkgbWFraW5nIHRoaXMgYSBudW1lcmljIHZhcmlhYmxlOgoKYGBge3IgY29lcmNlLWNoYXIsIGVycm9yPVRSVUV9CnggPC0gYXMubnVtZXJpYyh4KQpgYGAKClByaW50IG91dCBgeGAuCgpgYGB7cn0KeApgYGAKClIgaXMgdGVsbGluZyB1cyBpdCBkb2Vzbid0IGtub3cgaG93IHRvIGNvbnZlcnQgdGhpcyB0byBhIG51bWVyaWMgdmFyaWFibGUsIHNvIGl0IGhhcyByZXR1cm5lZCBgTkFgIGluc3RlYWQuCgpGb3IgcmVmZXJlbmNlLCBoZXJlJ3MgYSBzdW1tYXJ5IG9mIHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlIHR5cGVzLiAKCnwgVmFyaWFibGUgVHlwZSB8IERlZmluaXRpb24gfCBFeGFtcGxlcyB8IENvZXJjaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwgLS0tLS0tLS18CnwgYG51bWVyaWNgICAgICAgIHwgQW55IG51bWJlciB2YWx1ZSB8IGA1YDxicj5gNy41YCA8YnI+YC0xYHwgYGFzLm51bWVyaWMoKWAKfCBgaW50ZWdlcmAgICAgICAgfCBBbnkgX3dob2xlXyBudW1iZXIgdmFsdWUgKG5vIGRlY2ltYWxzKSB8IGA1YCA8YnI+IGAtMTAwYCB8IGBhcy5pbnRlZ2VyKClgCnxgY2hhcmFjdGVyYCAgICAgIHwgQW55IGNvbGxlY3Rpb24gb2YgY2hhcmFjdGVycyBkZWZpbmVkIHdpdGhpbiBfcXVvdGF0aW9uIG1hcmtzXy4gQWxzbyBrbm93biBhcyBhICJzdHJpbmciLiB8IGAiYSJgIChhIHNpbmdsZSBsZXR0ZXIpIDxicj5gInN0cmluZ29mbGV0dGVycyJgIChhIHdob2xlIGJ1bmNoIG9mIGNoYXJhY3RlcnMgcHV0IHRvZ2V0aGVyIGFzIG9uZSkgPGJyPiBgInN0cmluZyBvZiBsZXR0ZXJzIGFuZCBzcGFjZXMiYCA8YnI+IGAiNSJgIDxicj4gYCdzaW5nbGUgcXVvdGVzIGFyZSBhbHNvIGdvb2QnYCB8IGBhcy5jaGFyYWN0ZXIoKWAKfGBsb2dpY2FsYCAgICAgIHwgQSB2YWx1ZSBvZiBgVFJVRWAsIGBGQUxTRWAsIG9yIGBOQWAgfCBgVFJVRWAgPGJyPiBgRkFMU0VgIDxicj4gYE5BYCAobm90IGRlZmluZWQpIHwgYGFzLmxvZ2ljYWwoKWAgCnxgZmFjdG9yYCAgICAgICB8IEEgc3BlY2lhbCB0eXBlIG9mIHZhcmlhYmxlIHRoYXQgZGVub3RlcyBzcGVjaWZpYyBjYXRlZ29yaWVzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgfCAoc3RheSB0dW5lZC4uKSB8IGBhcy5mYWN0b3IoKWAKCiMjIyBWZWN0b3JzCgpZb3Ugd2lsbCBoYXZlIG5vdGljZWQgdGhhdCBhbGwgeW91ciBjb21wdXRhdGlvbnMgdGVuZCB0byBwb3AgdXAgd2l0aCBhIGBbMV1gIHByZWNlZGluZyB0aGVtIGluIFIncyBvdXRwdXQuIApUaGlzIGlzIGJlY2F1c2UsIGluIGZhY3QsIGFsbCAob2sgbW9zdGx5IGFsbCkgdmFyaWFibGVzIGFyZSBfYnkgZGVmYXVsdF8gIHZlY3RvcnMsIGFuZCBvdXIgYW5zd2VycyBhcmUgdGhlIGZpcnN0IChpbiB0aGVzZSBjYXNlcyBvbmx5KSB2YWx1ZSBpbiB0aGUgdmVjdG9yLiAKQXMgdmVjdG9ycyBnZXQgbG9uZ2VyLCBuZXcgaW5kZXggaW5kaWNhdG9ycyB3aWxsIGFwcGVhciBhdCB0aGUgc3RhcnQgb2YgbmV3IGxpbmVzLiAKCmBgYHtyfQojIFRoaXMgaXMgYWN0dWFsbHkgYW4gdmVjdG9yIHRoYXQgaGFzIG9uZSBpdGVtIGluIGl0Lgp4IDwtIDcKYGBgCgpgYGB7ciB2ZWN0b3ItbGVuZ3RofQojIFRoZSBsZW5ndGgoKSBmdW5jdGlvbnMgdGVsbHMgdXMgaG93IGxvbmcgYW4gdmVjdG9yIGlzOgpsZW5ndGgoeCkKYGBgCgpXZSBjYW4gZGVmaW5lIHZlY3RvcnMgd2l0aCB0aGUgZnVuY3Rpb24gYGMoKWAsIHdoaWNoIHN0YW5kcyBmb3IgImNvbWJpbmUiLiAKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIGNvbW1hLXNlcGFyYXRlZCBzZXQgb2YgdmFsdWVzIHRvIHBsYWNlIGluIHRoZSB2ZWN0b3IsIGFuZCByZXR1cm5zIHRoZSB2ZWN0b3IgaXRzZWxmOgoKYGBge3IgbWFrZS12ZWN0b3J9Cm15X251bWVyaWNfdmVjdG9yIDwtIGMoMSwgMSwgMiwgMywgNSwgOCwgMTMsIDIxKQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBidWlsZCBvbiB2ZWN0b3JzIGluIHBsYWNlIGJ5IHJlZGVmaW5pbmcgdGhlbToKCmBgYHtyIGZpYmJvbmFjY2ksIGxpdmUgPSBUUlVFfQojIGFkZCB0aGUgbmV4dCB0d28gRmlib25hY2NpIG51bWJlcnMgdG8gdGhlIHNlcmllcy4KbXlfbnVtZXJpY192ZWN0b3IgPC0gYyhteV9udW1lcmljX3ZlY3RvciwgMzQsIDU1KQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBwdWxsIG91dCBzcGVjaWZpYyBpdGVtcyBmcm9tIGFuIHZlY3RvciB1c2luZyBhIHByb2Nlc3MgY2FsbGVkIF9pbmRleGluZ18sIHdoaWNoIHVzZXMgYnJhY2tldHMgYFtdYCB0byBzcGVjaWZ5IHRoZSBwb3NpdGlvbiBvZiBhbiBpdGVtLiAKCmBgYHtyIHN1YnNldDF9CiMgR3JhYiB0aGUgZm91cnRoIHZhbHVlIGZyb20gbXlfbnVtZXJpY192ZWN0b3IKIyBUaGlzIGdpdmVzIHVzIGFuIHZlY3RvciBvZiBsZW5ndGggMSAKbXlfbnVtZXJpY192ZWN0b3JbNF0KYGBgCgpDb2xvbnMgYXJlIGFsc28gYSBuaWNlIHdheSB0byBxdWlja2x5IG1ha2Ugb3JkZXJlZCBudW1lcmljIHZlY3RvcnMKVXNlIGEgY29sb24gdG8gc3BlY2lmeSBhbiBpbmNsdXNpdmUgcmFuZ2Ugb2YgaW5kaWNlcwpUaGlzIHdpbGwgcmV0dXJuIGFuIHZlY3RvciB3aXRoIDIsIDMsIDQsIGFuZCA1LgoKYGBge3Igc3Vic2V0LW1hbnl9Cm15X251bWVyaWNfdmVjdG9yWzI6NV0KYGBgCgpPbmUgbWFqb3IgYmVuZWZpdCBvZiB2ZWN0b3JzIGlzIHRoZSBjb25jZXB0IG9mICoqdmVjdG9yaXphdGlvbioqLCB3aGVyZSBSIGJ5IGRlZmF1bHQgcGVyZm9ybXMgb3BlcmF0aW9ucyBvbiB0aGUgX2VudGlyZSB2ZWN0b3IgYXQgb25jZV8uIApGb3IgZXhhbXBsZSwgd2UgY2FuIGdldCB0aGUgbG9nIG9mIGFsbCBudW1iZXJzIDEtMjAgd2l0aCBhIHNpbmdsZSwgc2ltcGxlIGNhbGwsIGFuZCBtb3JlIQoKYGBge3IgdmVjdG9yaXplfQp2YWx1ZXNfMV90b18yMCA8LSAxOjIwCmBgYAoKCmBgYHtyIHZlY3Rvcml6ZS1sb2csIGxpdmUgPSBUUlVFfQojIGNhbGN1bGF0ZSB0aGUgbG9nIG9mIHZhbHVlc18xX3RvXzIwCmxvZyh2YWx1ZXNfMV90b18yMCkKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gYXBwbHkgbG9naWNhbCBleHByZXNzaW9ucyB0byB2ZWN0b3JzLCBqdXN0IGFzIHdlIGNhbiBkbyBmb3Igc2luZ2xlIHZhbHVlcy4KVGhlIG91dHB1dCBoZXJlIGlzIGEgbG9naWNhbCB2ZWN0b3IgdGVsbGluZyB1cyB3aGV0aGVyIGVhY2ggdmFsdWUgaW4gZXhhbXBsZV92ZWN0b3IgaXMgVFJVRSBvciBGQUxTRQoKYGBge3IgdmVjdG9yLWNvbXBhcmV9CiMgV2hpY2ggdmFsdWVzIGFyZSA8PSAzPwp2YWx1ZXNfMV90b18yMCA8PSAzCmBgYAoKVGhlcmUgYXJlIHNldmVyYWwga2V5IGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgdXNlZCBvbiB2ZWN0b3JzIGNvbnRhaW5pbmcgbnVtZXJpYyB2YWx1ZXMsIHNvbWUgb2Ygd2hpY2ggYXJlIGJlbG93LgoKKyBgbWVhbigpYDogVGhlIGF2ZXJhZ2UgdmFsdWUgaW4gdGhlIHZlY3RvcgorIGBtaW4oKWA6IFRoZSBtaW5pbXVtIHZhbHVlIGluIHRoZSB2ZWN0b3IKKyBgbWF4KClgOiBUaGUgbWF4aW11bSB2YWx1ZSBpbiB0aGUgdmVjdG9yCisgYHN1bSgpYDogVGhlIHN1bSBvZiBhbGwgdmFsdWVzIGluIHRoZSB2ZWN0b3IKCldlIGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyBvbiB0aGUgdmVjdG9yIGB2YWx1ZXNfMV90b18yMGAgd2UndmUgY3JlYXRlZC4gCgpgYGB7ciB2ZWN0b3ItZnVuY3N9Cm1lYW4odmFsdWVzXzFfdG9fMjApCgojIFRyeSBvdXQgc29tZSBvZiB0aGUgb3RoZXIgZnVuY3Rpb25zIHdlJ3ZlIGxpc3RlZCBhYm92ZSAKCmBgYAoKIyMjIEEgbm90ZSBvbiB2YXJpYWJsZSBuYW1pbmcKCldlIGhhdmUgbGVhcm5lZCBmdW5jdGlvbnMgc3VjaCBhcyBgY2AsIGBsZW5ndGhgLCBgc3VtYCwgYW5kIGV0Yy4gCkltYWdpbmUgZGVmaW5pbmcgYSB2YXJpYWJsZSBjYWxsZWQgYGNgOiBUaGlzIHdpbGwgd29yaywgYnV0IGl0IHdpbGwgbGVhZCB0byBhIApsb3Qgb2YgdW5pbnRlbmRlZCBidWdzLCBzbyBpdCdzIGJlc3QgdG8gYXZvaWQgdGhpcy4gCgojIyMgVGhlIGAlaW4lYCBsb2dpY2FsIG9wZXJhdG9yIAoKYCVpbiVgIGlzIHVzZWZ1bCBmb3IgZGV0ZXJtaW5pbmcgd2hldGhlciBhIGdpdmVuIGl0ZW0ocykgYXJlIGluIGFuIHZlY3Rvci4KCmBgYHtyIGluLW9wZXJhdG9yfQojIGlzIGA3YCBpbiBvdXIgdmVjdG9yPyAKNyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKYGBge3IgaW4yLCBsaXZlID0gVFJVRX0KIyBpcyBgNTBgIGluIG91ciB2ZWN0b3I/IAo1MCAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKV2UgY2FuIHRlc3QgYSB2ZWN0b3Igb2YgdmFsdWVzIGJlaW5nIHdpdGhpbiBhbm90aGVyIHZlY3RvciBvZiB2YWx1ZXMuIAoKYGBge3IgdmVjdG9yLWluLCBsaXZlID0gVFJVRX0KcXVlc3Rpb25fdmFsdWVzIDwtIGMoMTozLCA3LCA1MCkKIyBBcmUgdGhlc2UgdmFsdWVzIGluIG91ciB2ZWN0b3I/CnF1ZXN0aW9uX3ZhbHVlcyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKIyMgRGF0YSBmcmFtZXMKCl9EYXRhIGZyYW1lcyBhcmUgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBmb3IgZGF0YSBhbmFseXNpcyBpbiBSLl8gClRoZXkgYXJlIHRhYmxlcyB3aGljaCBjb25zaXN0IG9mIHJvd3MgYW5kIGNvbHVtbnMsIG11Y2ggbGlrZSBhIF9zcHJlYWRzaGVldF8uIApFYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlIHdoaWNoIGJlaGF2ZXMgYXMgYSBfdmVjdG9yXywgYW5kIGVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uLiAKV2Ugd2lsbCBiZWdpbiBvdXIgZXhwbG9yYXRpb24gd2l0aCBkYXRhc2V0IG9mIG1lYXN1cmVtZW50cyBmcm9tIHRocmVlIHBlbmd1aW4gc3BlY2llcyBtZWFzdXJlZCwgd2hpY2ggd2UgY2FuIGZpbmQgaW4gdGhlIFtgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2VdKGh0dHBzOi8vYWxsaXNvbmhvcnN0LmdpdGh1Yi5pby9wYWxtZXJwZW5ndWlucy8pLiAKV2UnbGwgdGFsayBtb3JlIGFib3V0IHBhY2thZ2VzIHNvb24hClRvIHVzZSB0aGlzIGRhdGFzZXQsIHdlIHdpbGwgbG9hZCBpdCBmcm9tIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UgdXNpbmcgYSBgOjpgIChtb3JlIG9uIHRoaXMgbGF0ZXIpIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lZCBgcGVuZ3VpbnNgIGluIG91ciBjdXJyZW50IGVudmlyb25tZW50LgoKYGBge3IgcGVuZ3Vpbi1saWJyYXJ5fQpwZW5ndWlucyA8LSBwYWxtZXJwZW5ndWluczo6cGVuZ3VpbnMKYGBgCgohW2RyYXdpbmdzIG9mIHBlbmd1aW4gc3BlY2llc10oZGlhZ3JhbXMvbHRlcl9wZW5ndWlucy5wbmcpIEFydHdvcmsgYnkgW0BhbGxpc29uX2hvcnN0XShodHRwczovL3R3aXR0ZXIuY29tL2FsbGlzb25faG9yc3QpCgojIyMgRXhwbG9yaW5nIGRhdGEgZnJhbWVzCgpUaGUgZmlyc3Qgc3RlcCB0byB1c2luZyBhbnkgZGF0YSBpcyB0byBsb29rIGF0IGl0ISEhIApSU3R1ZGlvIGNvbnRhaW5zIGEgc3BlY2lhbCBmdW5jdGlvbiBgVmlldygpYCB3aGljaCBhbGxvd3MgeW91IHRvIGxpdGVyYWxseSB2aWV3IGEgdmFyaWFibGUuCllvdSBjYW4gYWxzbyBjbGljayBvbiB0aGUgb2JqZWN0IGluIHRoZSBlbnZpcm9ubWVudCBwYW5lIHRvIHNlZSBpdHMgb3ZlcmFsbCBwcm9wZXJ0aWVzLCBvciBjbGljayB0aGUgdGFibGUgaWNvbiBvbiB0aGUgb2JqZWN0J3Mgcm93IHRvIGF1dG9tYXRpY2FsbHkgdmlldyB0aGUgdmFyaWFibGUuIAoKU29tZSB1c2VmdWwgZnVuY3Rpb25zIGZvciBleHBsb3Jpbmcgb3VyIGRhdGEgZnJhbWUgaW5jbHVkZToKCisgYGhlYWQoKWAgdG8gc2VlIHRoZSBmaXJzdCA2IHJvd3Mgb2YgYSBkYXRhIGZyYW1lLiBBZGRpdGlvbmFsIGFyZ3VtZW50cyBzdXBwbGllZCBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2Ygcm93cy4KKyBgdGFpbCgpYCB0byBzZWUgdGhlIGxhc3QgNiByb3dzIG9mIGEgZGF0YSBmcmFtZS4gQWRkaXRpb25hbCBhcmd1bWVudHMgc3VwcGxpZWQgY2FuIGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MuCisgYG5hbWVzKClgIHRvIHNlZSB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBkYXRhIGZyYW1lLgorIGBucm93KClgIHRvIHNlZSBob3cgbWFueSByb3dzIGFyZSBpbiB0aGUgZGF0YSBmcmFtZQorIGBuY29sKClgIHRvIHNlZSBob3cgbWFueSBjb2x1bW5zIGFyZSBpbiB0aGUgZGF0YSBmcmFtZS4KCldlIGNhbiBhZGRpdGlvbmFsbHkgZXhwbG9yZSBfb3ZlcmFsbCBwcm9wZXJ0aWVzXyBvZiB0aGUgZGF0YSBmcmFtZSB3aXRoIHR3byBkaWZmZXJlbnQgZnVuY3Rpb25zOiBgc3VtbWFyeSgpYCBhbmQgYHN0cigpYC4KClRoaXMgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGNvbHVtbjoKCmBgYHtyIHBlbmd1aW5zLXN1bW1hcnl9CnN1bW1hcnkocGVuZ3VpbnMpCmBgYAoKVGhpcyBwcm92aWRlcyBhIHNob3J0IHZpZXcgb2YgdGhlICoqc3RyKip1Y3R1cmUgYW5kIGNvbnRlbnRzIG9mIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgcGVuZ3VpbnMtc3RyfQpzdHIocGVuZ3VpbnMpCmBgYAoKWW91J2xsIG5vdGljZSB0aGF0IHRoZSBjb2x1bW4gYHNwZWNpZXNgIGlzIGEgX2ZhY3Rvcl86IFRoaXMgaXMgYSBzcGVjaWFsIHR5cGUgb2YgY2hhcmFjdGVyIHZhcmlhYmxlIHRoYXQgcmVwcmVzZW50cyBkaXN0aW5jdCBjYXRlZ29yaWVzIGtub3duIGFzICJsZXZlbHMiLiAKV2UgaGF2ZSBsZWFybmVkIGhlcmUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgbGV2ZWxzIGluIHRoZSBgc3BlY2llc2AgY29sdW1uOiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvby4KV2UgbWlnaHQgd2FudCB0byBleHBsb3JlIGluZGl2aWR1YWwgY29sdW1ucyBvZiB0aGUgZGF0YSBmcmFtZSBtb3JlIGluLWRlcHRoLiAKV2UgY2FuIGV4YW1pbmUgaW5kaXZpZHVhbCBjb2x1bW5zIHVzaW5nIHRoZSBkb2xsYXIgc2lnbiBgJGAgdG8gc2VsZWN0IG9uZSBieSBuYW1lOgoKYGBge3IgcGVuZ3VpbnMtc3Vic2V0fQojIEV4dHJhY3QgYmlsbF9sZW5ndGhfbW0gYXMgYSB2ZWN0b3IKcGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0KCiMgaW5kZXhpbmcgb3BlcmF0b3JzIGNhbiBiZSB1c2VkIG9uIHRoZXNlIHZlY3RvcnMgdG9vCnBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tWzE6MTBdCmBgYAoKV2UgY2FuIHBlcmZvcm0gb3VyIHJlZ3VsYXIgdmVjdG9yIG9wZXJhdGlvbnMgb24gY29sdW1ucyBkaXJlY3RseS4KCmBgYHtyIHBlbmd1aW5zLWNvbC1tZWFuLCBsaXZlID0gVFJVRX0KIyBjYWxjdWxhdGUgdGhlIG1lYW4gb2YgdGhlIGJpbGxfbGVuZ3RoX21tIGNvbHVtbgptZWFuKHBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tLAogICAgIG5hLnJtID0gVFJVRSkgIyByZW1vdmUgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBtZWFuCmBgYAoKV2UgY2FuIGFsc28gY2FsY3VsYXRlIHRoZSBmdWxsIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBzaW5nbGUgY29sdW1uIGRpcmVjdGx5LiAKCmBgYHtyIHBlbmd1aW5zLWNvbC1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzaG93IGEgc3VtbWFyeSBvZiB0aGUgYmlsbF9sZW5ndGhfbW0gY29sdW1uCnN1bW1hcnkocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0pCmBgYAoKRXh0cmFjdCBgc3BlY2llc2AgYXMgYSB2ZWN0b3IgYW5kIHN1YnNldCBpdCB0byBzZWUgYSBwcmV2aWV3LgoKYGBge3IgcGVuZ3VpbnMtY29sLXN1YnNldCwgbGl2ZSA9IFRSVUV9CiMgZ2V0IHRoZSBmaXJzdCAxMCB2YWx1ZXMgb2YgdGhlIHNwZWNpZXMgY29sdW1uCnBlbmd1aW5zJHNwZWNpZXNbMToxMF0KYGBgCgpBbmQgdmlldyBpdHMgX2xldmVsc18gd2l0aCB0aGUgYGxldmVscygpYCBmdW5jdGlvbi4KCmBgYHtyIHBlbmd1aW4tbGV2ZWxzfQpsZXZlbHMocGVuZ3VpbnMkc3BlY2llcykKYGBgCgojIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCkluIG1hbnkgc2l0dWF0aW9ucywgd2Ugd2lsbCBiZSByZWFkaW5nIGluIHRhYnVsYXIgZGF0YSBmcm9tIGEgZmlsZSBhbmQgdXNpbmcgaXQgYXMgYSBkYXRhIGZyYW1lLiAKVG8gcHJhY3RpY2UsIHdlIHdpbGwgcmVhZCBpbiBhIGZpbGUgd2Ugd2lsbCBiZSB1c2luZyBpbiB0aGUgbmV4dCBub3RlYm9vayBhcyB3ZWxsLCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAsIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gdGhlIGxvY2F0aW9uIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuCgpIZXJlIHdlIHdpbGwgdXNlIGEgZnVuY3Rpb24sIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkJlZm9yZSB3ZSBhcmUgYWJsZSB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBoYXZlIHRvIGxvYWQgdGhlIHBhY2thZ2UgdXNpbmcgYGxpYnJhcnkoKWAuIAoKYGBge3IgcmVhZHJ9CmxpYnJhcnkocmVhZHIpCmBgYAoKYGZpbGUucGF0aCgpYCBjcmVhdGVzIGEgcHJvcGVybHkgZm9ybWF0dGVkIGZpbGUgcGF0aCBieSBhZGRpbmcgYSBwYXRoIHNlcGFyYXRvciAoYC9gIG9uIE1hYyBhbmQgTGludXggb3BlcmF0aW5nIHN5c3RlbXMsIHRoZSBsYXR0ZXIgb2Ygd2hpY2ggaXMgdGhlIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCBvdXIgUlN0dWRpbyBTZXJ2ZXIgcnVucyBvbikgYmV0d2VlbiBzZXBhcmF0ZSBmb2xkZXJzIG9yIGRpcmVjdG9yaWVzLgpCZWNhdXNlIGZpbGUgcGF0aCBzZXBhcmF0b3JzIGNhbiBkaWZmZXIgYmV0d2VlbiB5b3VyIGNvbXB1dGVyIGFuZCB0aGUgY29tcHV0ZXIgb2Ygc29tZW9uZSB3aG8gd2FudHMgdG8gdXNlIHlvdXIgY29kZSwgd2UgdXNlIGBmaWxlLnBhdGgoKWAgaW5zdGVhZCBvZiB0eXBpbmcgb3V0IGAiZGF0YS9nZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2ImAuCkVhY2ggX2FyZ3VtZW50XyB0byBgZmlsZS5wYXRoKClgIGlzIGEgZGlyZWN0b3J5IG9yIGZpbGUgbmFtZS4KWW91J2xsIG5vdGljZSBlYWNoIGFyZ3VtZW50IGlzIGluIHF1b3Rlcywgd2Ugc3BlY2lmeSBgZGF0YWAgZmlyc3QgYmVjYXVzZSB0aGUgZmlsZSwgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGlzIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKCmBgYHtyIGZpbGUucGF0aH0KZmlsZS5wYXRoKCJkYXRhIiwgImdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3YiKQpgYGAKCkFzIHlvdSBjYW4gc2VlIGFib3ZlLCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgYGZpbGUucGF0aCgpYCBpcyB0aGF0IGl0IF9jcmVhdGVzIGEgc3RyaW5nXyB3aXRoIGFuIGFjY3VyYXRlbHktZm9ybWF0dGVkIHBhdGggZm9yIHlvdXIgZmlsZSBzeXN0ZW0uClRoaXMgc3RyaW5nIGNhbiBiZSB1c2VkIG1vdmluZyBmb3J3YXJkIHdoZW4geW91IG5lZWQgdG8gcmVmZXIgdG8gdGhlIHBhdGggdG8geW91ciBmaWxlLgpMZXQncyBnbyBhaGVhZCBhbmQgc3RvcmUgdGhpcyBmaWxlIHBhdGggYXMgYSB2YXJpYWJsZSBpbiBvdXIgZW52aXJvbm1lbnQuIAoKYGBge3IgZmlsZS5wYXRoLXZhcmlhYmxlfQpnZW5lX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIpCmBgYAoKTm93IHdlIGFyZSByZWFkeSB0byB1c2UgYHJlYWRfdHN2KClgIHRvIHJlYWQgdGhlIGZpbGUgaW50byBSLgpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2lsbCBiZSBzdG9yZWQgaW4gYSB2YXJpYWJsZSBuYW1lZCBgc3RhdHNfZGZgLgpOb3RlIHRoZSBgPC1gIChhc3NpZ25tZW50IG9wZXJhdG9yISkgaXMgcmVzcG9uc2libGUgZm9yIHNhdmluZyB0aGlzIHRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuIAoKYGBge3IgcmVhZC1zdGF0c30KIyByZWFkIGluIHRoZSBmaWxlIGBnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2YCBmcm9tIHRoZSBkYXRhIGRpcmVjdG9yeQpzdGF0c19kZiA8LSByZWFkX3RzdihnZW5lX2ZpbGVfcGF0aCkKYGBgCgpUYWtlIGEgbG9vayBhdCB5b3VyIGVudmlyb25tZW50IHBhbmVsIHRvIHNlZSB3aGF0IGBzdGF0c19kZmAgbG9va3MgbGlrZS4gCldlIGNhbiBhbHNvIHByaW50IG91dCBhIHByZXZpZXcgb2YgdGhlIGBzdGF0c19kZmAgZGF0YSBmcmFtZSBoZXJlLiAKCmBgYHtyIHNob3ctc3RhdHMsIGxpdmUgPSBUUlVFfQojIGRpc3BsYXkgc3RhdHNfZGYKc3RhdHNfZGYKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpBdCB0aGUgZW5kIG9mIGV2ZXJ5IG5vdGVib29rLCB5b3Ugd2lsbCBzZWUgdXMgcHJpbnQgb3V0IGBzZXNzaW9uSW5mb2AuIApUaGlzIGFpZHMgaW4gdGhlIHJlcHJvZHVjaWJpbGl0eSBvZiB5b3VyIGNvZGUgYnkgc2hvd2luZyBleGFjdGx5IHdoYXQgcGFja2FnZXMgCmFuZCB2ZXJzaW9ucyB3ZXJlIGJlaW5nIHVzZWQgdGhlIGxhc3QgdGltZSB0aGUgbm90ZWJvb2sgd2FzIHJ1bi4KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=
    diff --git a/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd b/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd index 2e854b26..5033057d 100644 --- a/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd +++ b/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd @@ -34,7 +34,7 @@ We performed three sets of contrasts: - [ggplot2 website](https://ggplot2.tidyverse.org/) - [Handy cheatsheet for ggplot2 (pdf)](https://github.com/rstudio/cheatsheets/raw/main/data-visualization.pdf) - [_Data Visualization, A practical introduction_](https://socviz.co/) -- [Data visualization chapter of _R for Data Science_](https://r4ds.had.co.nz/data-visualisation.html) +- [Data visualization chapter of _R for Data Science_](https://r4ds.hadley.nz/data-visualize.html) - [ggplot2 online tutorial](http://r-statistics.co/Complete-Ggplot2-Tutorial-Part1-With-R-Code.html) ## Set Up diff --git a/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html b/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html index 929e4c1a..f60de6a1 100644 --- a/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html +++ b/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html @@ -2917,7 +2917,7 @@

    Objectives

    cheatsheet for ggplot2 (pdf)
  • Data Visualization, A practical introduction
  • -
  • Data +
  • Data visualization chapter of R for Data Science
  • ggplot2 online tutorial
  • @@ -3521,8 +3521,8 @@

    Session Info

    # Print out the versions and packages we are using in this session
     sessionInfo()
    - -
    R version 4.4.0 (2024-04-24)
    +
    +
    R version 4.4.1 (2024-06-14)
     Platform: x86_64-pc-linux-gnu
     Running under: Ubuntu 22.04.4 LTS
     
    @@ -3552,23 +3552,23 @@ 

    Session Info

    loaded via a namespace (and not attached): [1] sass_0.4.9 utf8_1.2.4 generics_0.1.3 stringi_1.8.3 [5] hms_1.1.3 digest_0.6.35 magrittr_2.0.3 evaluate_0.23 - [9] grid_4.4.0 timechange_0.3.0 fastmap_1.1.1 jsonlite_1.8.8 + [9] grid_4.4.1 timechange_0.3.0 fastmap_1.1.1 jsonlite_1.8.8 [13] fansi_1.0.6 scales_1.3.0 textshaping_0.3.7 getopt_1.20.4 [17] jquerylib_0.1.4 cli_3.6.2 crayon_1.5.2 rlang_1.1.3 [21] bit64_4.0.5 munsell_0.5.1 withr_3.0.0 cachem_1.0.8 -[25] yaml_2.3.8 parallel_4.4.0 tools_4.4.0 tzdb_0.4.0 +[25] yaml_2.3.8 parallel_4.4.1 tools_4.4.1 tzdb_0.4.0 [29] colorspace_2.1-0 vctrs_0.6.5 R6_2.5.1 lifecycle_1.0.4 [33] bit_4.0.5 vroom_1.6.5 ragg_1.3.0 pkgconfig_2.0.3 [37] pillar_1.9.0 bslib_0.7.0 gtable_0.3.5 glue_1.7.0 [41] systemfonts_1.0.6 highr_0.10 xfun_0.43 tidyselect_1.2.1 [45] knitr_1.46 farver_2.1.1 htmltools_0.5.8.1 labeling_0.4.3 -[49] rmarkdown_2.26 compiler_4.4.0
    +[49] rmarkdown_2.26 compiler_4.4.1
    -
    LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdDIiCmF1dGhvcjogIkNDREwgZm9yIEFMU0YiCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBMb2FkIGFuZCB1c2UgUiBwYWNrYWdlcwotIFJlYWQgaW4gYW5kIHBlcmZvcm0gc2ltcGxlIG1hbmlwdWxhdGlvbnMgb2YgZGF0YSBmcmFtZXMKLSBVc2UgYGdncGxvdDJgIHRvIHBsb3QgYW5kIHZpc3VhbGl6ZSBkYXRhCi0gQ3VzdG9taXplIHBsb3RzIHVzaW5nIGZlYXR1cmVzIG9mIGBnZ3Bsb3QyYAoKLS0tCgpXZSdsbCB1c2UgYSByZWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHRvIGdldCBjb21mb3J0YWJsZSBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdXNpbmcgZ2dwbG90Mi4KV2UndmUgW3BlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlc10oLi9zY3JpcHRzLzAwLXNldHVwLWludHJvLXRvLVIuUikgb24gYSBwcmUtcHJvY2Vzc2VkIFthc3Ryb2N5dG9tYSBtaWNyb2FycmF5IGRhdGFzZXRdKGh0dHBzOi8vd3d3LnJlZmluZS5iaW8vZXhwZXJpbWVudHMvR1NFNDQ5NzEvZ2VuZS1leHByZXNzaW9uLWRhdGEtZnJvbS1waWxvY3l0aWMtYXN0cm9jeXRvbWEtdHVtb3VyLXNhbXBsZXMtYW5kLW5vcm1hbC1jZXJlYmVsbHVtLWNvbnRyb2xzKS4KV2UnbGwgc3RhcnQgYnkgbWFraW5nIGEgdm9sY2FubyBwbG90IG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBmcm9tIHRoaXMgZXhwZXJpbWVudC4KV2UgcGVyZm9ybWVkIHRocmVlIHNldHMgb2YgY29udHJhc3RzOgoKMSkgYHNleGAgY2F0ZWdvcnkgY29udHJhc3Rpbmc6IGBNYWxlYCB2cyBgRmVtYWxlYAoyKSBgdGlzc3VlYCBjYXRlZ29yeSBjb250cmFzdGluZyA6IGBQaWxvY3l0aWMgYXN0cm9jeXRvbWEgdHVtb3JgIHNhbXBsZXMgdnMgYG5vcm1hbCBjZXJlYmVsbHVtYCBzYW1wbGVzCjMpIEFuIGludGVyYWN0aW9uIG9mIGJvdGggYHNleGAgYW5kIGB0aXNzdWVgLgoKKipNb3JlIGdncGxvdDIgcmVzb3VyY2VzOioqCgotIFtnZ3Bsb3QyIHdlYnNpdGVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykKLSBbSGFuZHkgY2hlYXRzaGVldCBmb3IgZ2dwbG90MiAocGRmKV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS12aXN1YWxpemF0aW9uLnBkZikKLSBbX0RhdGEgVmlzdWFsaXphdGlvbiwgQSBwcmFjdGljYWwgaW50cm9kdWN0aW9uX10oaHR0cHM6Ly9zb2N2aXouY28vKQotIFtEYXRhIHZpc3VhbGl6YXRpb24gY2hhcHRlciBvZiBfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbCkKLSBbZ2dwbG90MiBvbmxpbmUgdHV0b3JpYWxdKGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vQ29tcGxldGUtR2dwbG90Mi1UdXRvcmlhbC1QYXJ0MS1XaXRoLVItQ29kZS5odG1sKQoKIyMgU2V0IFVwCgpXZSBzYXZlZCB0aGVzZSByZXN1bHRzIHRvIGEgdGFiIHNlcGFyYXRlZCB2YWx1ZXMgKFRTVikgZmlsZSBjYWxsZWQgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgLgpJdCdzIGJlZW4gc2F2ZWQgdG8gdGhlIGBkYXRhYCBmb2xkZXIuCkZpbGUgcGF0aHMgYXJlIHJlbGF0aXZlIHRvIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuClNvIHdlIGNhbiByZWZlcmVuY2UgaXQgbGF0ZXIsIGxldCdzIG1ha2UgYSB2YXJpYWJsZSB3aXRoIG91ciBkYXRhIGRpcmVjdG9yeSBuYW1lLgoKYGBge3J9CmRhdGFfZGlyIDwtICJkYXRhIgpgYGAKCkxldCdzIGRlY2xhcmUgb3VyIG91dHB1dCBmb2xkZXIgbmFtZSBhcyBpdHMgb3duIHZhcmlhYmxlLgoKYGBge3J9CnBsb3RzX2RpciA8LSAicGxvdHMiCmBgYAoKV2UgY2FuIGFsc28gY3JlYXRlIGEgZGlyZWN0b3J5IGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdC4KClRoZXJlJ3MgYSBjb3VwbGUgd2F5cyB0aGF0IHdlIGNhbiBjcmVhdGUgdGhhdCBkaXJlY3RvcnkgZnJvbSB3aXRoaW4gUi4KT25lIHdheSBpcyB0byB1c2UgdGhlIGJhc2UgUiBmdW5jdGlvbiBgZGlyLmNyZWF0ZSgpYCwgd2hpY2ggKGFzIHRoZSBuYW1lIHN1Z2dlc3RzKSB3aWxsIGNyZWF0ZSBhIGRpcmVjdG9yeS4KQnV0IHRoaXMgZnVuY3Rpb24gYXNzdW1lcyB0aGF0IHRoZSBkaXJlY3RvcnkgZG9lcyBub3QgeWV0IGV4aXN0LCBhbmQgaXQgd2lsbCB0aHJvdyBhbiBlcnJvciBpZiB5b3UgdHJ5IHRvIGNyZWF0ZSBhIGRpcmVjdG9yeSB0aGF0IGFscmVhZHkgZXhpc3RzLgpUbyBhdm9pZCB0aGlzIGVycm9yLCB3ZSBjYW4gcGxhY2UgdGhlIGRpcmVjdG9yeSBjcmVhdGlvbiBpbnNpZGUgYW4gYGlmYCBzdGF0ZW1lbnQsIHNvIHRoZSBjb2RlIHdpbGwgb25seSBydW4gaWYgdGhlIGRpcmVjdG9yeSBkb2VzIG5vdCB5ZXQgZXhpc3Q6CgpgYGB7ciBjcmVhdGVpZn0KIyBUaGUgaWYgc3RhdGVtZW50IGhlcmUgdGVzdHMgd2hldGhlciB0aGUgcGxvdCBkaXJlY3RvcnkgZXhpc3RzIGFuZAojIG9ubHkgZXhlY3V0ZXMgdGhlIGV4cHJlc3Npb25zIGJldHdlZW4gdGhlIGJyYWNlcyBpZiBpdCBkb2VzIG5vdC4KaWYgKCFkaXIuZXhpc3RzKHBsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHBsb3RzX2RpcikKfQpgYGAKCkluIHRoaXMgbm90ZWJvb2sgd2Ugd2lsbCBiZSB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgVGlkeXZlcnNlIHNldCBvZiBwYWNrYWdlcywgc28gd2UgbmVlZCB0byBsb2FkIGluIHRob3NlIGZ1bmN0aW9ucyB1c2luZyBgbGlicmFyeSgpYC4KV2UgY291bGQgbG9hZCB0aGUgaW5kaXZpZHVhbCBwYWNrYWdlcyB3ZSBuZWVkIG9uZSBhdCBhIHRpbWUsIGJ1dCBpdCBpcyBjb252ZW5pZW50IGZvciBub3cgdG8gbG9hZCB0aGVtIGFsbCB3aXRoIHRoZSBgdGlkeXZlcnNlYCAicGFja2FnZSwiIHdoaWNoIGdyb3VwcyBtYW55IG9mIHRoZW0gdG9nZXRoZXIgYXMgYSBzaG9ydGN1dC4KS2VlcCBhIGxvb2sgb3V0IGZvciB3aGVyZSB3ZSB0ZWxsIHlvdSB3aGljaCBpbmRpdmlkdWFsIHBhY2thZ2UgZGlmZmVyZW50IGZ1bmN0aW9ucyBjb21lIGZyb20uCgpgYGB7ciB0aWR5dmVyc2V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIFJlYWQgaW4gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHJlc3VsdHMgZmlsZQoKSGVyZSB3ZSBhcmUgdXNpbmcgYSBgdGlkeXZlcnNlYCBmdW5jdGlvbiBgcmVhZF90c3YoKWAgZnJvbSB0aGUgYHJlYWRyYCBwYWNrYWdlLgpMaWtlIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgbm90ZWJvb2ssIHdlIHdpbGwgc3RvcmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFzIGBzdGF0c19kZmAuCgpgYGB7ciByZWFkLXN0YXRzfQojIHJlYWQgaW4gdGhlIGZpbGUgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGZyb20gdGhlIGRhdGEgZGlyZWN0b3J5CnN0YXRzX2RmIDwtIHJlYWRfdHN2KGZpbGUucGF0aCgKICBkYXRhX2RpciwKICAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIKKSkKYGBgCgpXZSBjYW4gdGFrZSBhIGxvb2sgYXQgYSBjb2x1bW4gaW5kaXZpZHVhbGx5IGJ5IHVzaW5nIGEgYCRgLgpOb3RlIHdlIGFyZSB1c2luZyBgaGVhZCgpYCBzbyB0aGUgd2hvbGUgdGhpbmcgZG9lc24ndCBwcmludCBvdXQuCgpgYGB7ciBjb2x1bW59CmhlYWQoc3RhdHNfZGYkY29udHJhc3QpCmBgYAoKSWYgd2Ugd2FudCB0byBzZWUgYSBzcGVjaWZpYyBzZXQgb2YgdmFsdWVzLCB3ZSBjYW4gdXNlIGJyYWNrZXRzIHdpdGggdGhlIGluZGljZXMgb2YgdGhlIHZhbHVlcyB3ZSdkIGxpa2UgcmV0dXJuZWQuCgpgYGB7cn0Kc3RhdHNfZGYkYXZnX2V4cHJlc3Npb25bNjoxMF0KYGBgCgpMZXQncyBsb29rIGF0IHNvbWUgYmFzaWMgc3RhdGlzdGljcyBmcm9tIHRoZSBkYXRhIHNldCB1c2luZyBgc3VtbWFyeSgpYAoKYGBge3Igc3RhdHMtc3VtbWFyeSwgbGl2ZSA9IFRSVUV9CiMgc3VtbWFyeSBvZiBzdGF0c19kZgpzdW1tYXJ5KHN0YXRzX2RmKQpgYGAKClRoZSBzdGF0aXN0aWNzIGZvciBgY29udHJhc3RgIGFyZSBub3QgdmVyeSBpbmZvcm1hdGl2ZSwgc28gbGV0J3MgZG8gdGhhdCBhZ2FpbiB3aXRoIGp1c3QgdGhlIGBjb250cmFzdGAgY29sdW1uIGFmdGVyIGNvbnZlcnRpbmcgaXQgdG8gYSBgZmFjdG9yYApgYGB7ciBmYWN0b3Itc3VtbWFyeSwgbGl2ZSA9IFRSVUV9CiMgc3VtbWFyeSBvZiBgc3RhdHNfZGYkY29udHJhc3RgIGFzIGEgZmFjdG9yCnN1bW1hcnkoYXMuZmFjdG9yKHN0YXRzX2RmJGNvbnRyYXN0KSkKYGBgCgojIyBTZXQgdXAgdGhlIGRhdGFzZXQKCkJlZm9yZSB3ZSBtYWtlIG91ciBwbG90LCB3ZSB3YW50IHRvIGNhbGN1bGF0ZSBhIHNldCBvZiBuZXcgdmFsdWVzIGZvciBlYWNoIHJvdzsgdHJhbnNmb3JtYXRpb25zIG9mIHRoZSByYXcgc3RhdGlzdGljcyBpbiBvdXIgdGFibGUuClRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgYSBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgY2FsbGVkIGBtdXRhdGUoKWAgdG8gbWFrZSBhIG5ldyBjb2x1bW4gb2YgLWxvZzEwIHAgdmFsdWVzLgoKYGBge3IgbXV0YXRlfQojIGFkZCBhIGBuZWdfbG9nMTBfcGAgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCnN0YXRzX2RmIDwtIG11dGF0ZShzdGF0c19kZiwgIyBkYXRhIGZyYW1lIHdlJ2QgbGlrZSB0byBhZGQgYSB2YXJpYWJsZSB0bwogIG5lZ19sb2cxMF9wID0gLWxvZzEwKHBfdmFsdWUpICMgY29sdW1uIG5hbWUgYW5kIHZhbHVlcwopCmBgYAoKTGV0J3MgZmlsdGVyIHRvIG9ubHkgYG1hbGVfZmVtYWxlYCBjb250cmFzdCBkYXRhLgpGaXJzdCBsZXQncyB0cnkgb3V0IGEgbG9naWNhbCBleHByZXNzaW9uOgoKYGBge3IgZXZhbCA9IEZBTFNFfQpzdGF0c19kZiRjb250cmFzdCA9PSAibWFsZV9mZW1hbGUiCmBgYAoKTm93IHdlIGNhbiB0cnkgb3V0IHRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uLgpOb3RpY2UgdGhhdCB3ZSBhcmUgbm90IGFzc2lnbmluZyB0aGUgcmVzdWx0cyB0byBhIHZhcmlhYmxlLCBzbyB0aGlzIGZpbHRlcmVkIGRhdGFzZXQgd2lsbCBub3QgYmUgc2F2ZWQgdG8gdGhlIGVudmlyb25tZW50LgoKYGBge3IgZmlsdGVyLCBsaXZlID0gVFJVRX0KIyBmaWx0ZXIgc3RhdHNfZGYgdG8gIm1hbGVfZmVtYWxlIiBvbmx5CmZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIikKYGBgCgpOb3cgd2UgY2FuIGFzc2lnbiB0aGUgcmVzdWx0cyB0byBhIG5ldyBkYXRhIGZyYW1lOiBgbWFsZV9mZW1hbGVfZGZgLgoKYGBge3IgZmlsdGVyLXNhdmUsIGxpdmUgPSBUUlVFfQojIGZpbHRlciBhbmQgc2F2ZSB0byBtYWxlX2ZlbWFsZV9kZgptYWxlX2ZlbWFsZV9kZiA8LSBmaWx0ZXIoc3RhdHNfZGYsIGNvbnRyYXN0ID09ICJtYWxlX2ZlbWFsZSIpCmBgYAoKIyMgUGxvdHRpbmcgdGhpcyBkYXRhCgpMZXQncyBtYWtlIGEgdm9sY2FubyBwbG90IHdpdGggdGhpcyBkYXRhLgpGaXJzdCBsZXQncyB0YWtlIGEgbG9vayBhdCBvbmx5IHRoZSB0dW1vciB2cy4gbm9ybWFsIGNvbXBhcmlzb24uCkxldCdzIHNhdmUgdGhpcyBhcyBhIHNlcGFyYXRlIGRhdGEgZnJhbWUgYnkgYXNzaWduaW5nIGl0IGEgbmV3IG5hbWUuCgpgYGB7ciBmaWx0ZXItdHVtb3J9CnR1bW9yX25vcm1hbF9kZiA8LSBmaWx0ZXIoc3RhdHNfZGYsIGNvbnRyYXN0ID09ICJhc3Ryb2N5dG9tYV9ub3JtYWwiKQpgYGAKClRvIG1ha2UgdGhpcyBwbG90IHdlIHdpbGwgYmUgdXNpbmcgZnVuY3Rpb25zIGZyb20gdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlLCB0aGUgbWFpbiBwbG90dGluZyBwYWNrYWdlIG9mIHRoZSB0aWR5dmVyc2UuCldlIHVzZSB0aGUgZmlyc3QgZnVuY3Rpb24sIGBnZ3Bsb3QoKWAgdG8gZGVmaW5lIHRoZSBkYXRhIHRoYXQgd2lsbCBiZSBwbG90dGVkLgpSZW1lbWJlciwgdGhlIG5hbWUgb2YgdGhpcyBwYWNrYWdlIGlzIGBnZ3Bsb3QyYCwgYnV0IHRoZSBmdW5jdGlvbiB3ZSB1c2UgaXMgY2FsbGVkIGBnZ3Bsb3QoKWAgd2l0aG91dCB0aGUgYDJgLgpgZ2dwbG90KClgIHRha2VzIHR3byBtYWluIGFyZ3VtZW50czoKCjEuIGBkYXRhYCwgd2hpY2ggaXMgdGhlIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUgZGF0YSB3ZSB3YW50IHRvIHBsb3QuCjIuIGBtYXBwaW5nYCwgd2hpY2ggaXMgYSBzcGVjaWFsIGxpc3QgbWFkZSB3aXRoIHRoZSBgYWVzKClgIGZ1bmN0aW9uIHRvIGRlc2NyaWJlIHdoaWNoIHZhbHVlcyB3aWxsIGJlIHVzZWQgZm9yIGVhY2ggKiphZXMqKnRoZXRpYyBjb21wb25lbnQgb2YgdGhlIHBsb3QsIHN1Y2ggYXMgdGhlIHggYW5kIHkgY29vcmRpbmF0ZXMgb2YgZWFjaCBwb2ludC4KKElmIHlvdSBmaW5kIGNhbGxpbmcgdGhpbmdzIGxpa2UgdGhlIHggYW5kIHkgY29vcmRpbmF0ZXMgImFlc3RoZXRpY3MiIGNvbmZ1c2luZywgZG9uJ3Qgd29ycnksIHlvdSBhcmUgbm90IGFsb25lLikKU3BlY2lmaWNhbGx5LCB0aGUgYGFlcygpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIHNwZWNpZnkgdGhhdCBhIGdpdmVuIGNvbHVtbiAodmFyaWFibGUpIGluIHlvdXIgZGF0YSBmcmFtZSBiZSBtYXBwZWQgdG8gYSBnaXZlbiBhZXN0aGV0aWMgY29tcG9uZW50IG9mIHRoZSBwbG90LgoKCmBgYHtyIGdncGxvdC1iYXNlfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLCAjIFRoaXMgZmlyc3QgYXJndW1lbnQgaXMgdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgZGF0YSB3ZSB3YW50IHRvIHBsb3QKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLCAjIFRoaXMgaXMgdGhlIGNvbHVtbiBuYW1lIG9mIHRoZSB2YWx1ZXMgd2Ugd2FudCB0byB1c2UKICAgICMgZm9yIHRoZSB4IGNvb3JkaW5hdGVzCiAgICB5ID0gbmVnX2xvZzEwX3AKICApICMgVGhpcyBpcyB0aGUgY29sdW1uIG5hbWUgb2YgdGhlIGRhdGEgd2Ugd2FudCBmb3IgdGhlIHktYXhpcwopCmBgYAoKWW91J2xsIG5vdGljZSB0aGlzIHBsb3QgZG9lc24ndCBoYXZlIGFueXRoaW5nIG9uIGl0IGJlY2F1c2Ugd2UgaGF2ZW4ndApzcGVjaWZpZWQgYSBwbG90IHR5cGUgeWV0LgpUbyBkbyB0aGF0LCB3ZSB3aWxsIGFkZCBhbm90aGVyIGdncGxvdCBsYXllciB3aXRoIGArYCB3aGljaCB3aWxsIHNwZWNpZnkgZXhhY3RseSB3aGF0IHdlIHdhbnQgdG8gcGxvdC4KQSB2b2xjYW5vIHBsb3QgaXMgYSBzcGVjaWFsIGtpbmQgb2Ygc2NhdHRlciBwbG90LCBzbyB0byBtYWtlIHRoYXQgd2Ugd2lsbCB3YW50IHRvIHBsb3QgaW5kaXZpZHVhbCBwb2ludHMsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIGBnZW9tX3BvaW50KClgLgoKYGBge3IgZ2dwbG90LXBvaW50cywgbGl2ZSA9IFRSVUV9CiMgVGhpcyBmaXJzdCBwYXJ0IGlzIHRoZSBzYW1lIGFzIGJlZm9yZQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AKICApCikgKwogICMgTm93IHdlIGFyZSBhZGRpbmcgb24gYSBsYXllciB0byBzcGVjaWZ5IHdoYXQga2luZCBvZiBwbG90IHdlIHdhbnQKICBnZW9tX3BvaW50KCkKYGBgCgpIZXJlJ3MgYSBicmllZiBzdW1tYXJ5IG9mIGdncGxvdDIgc3RydWN0dXJlLgohW2dncGxvdDIgc3RydWN0dXJlXShkaWFncmFtcy9nZ3Bsb3Rfc3RydWN0dXJlLnBuZykKCiMjIyBBZGp1c3Qgb3VyIGdncGxvdAoKTm93IHRoYXQgd2UgaGF2ZSBhIGJhc2UgcGxvdCB0aGF0IHNob3dzIG91ciBkYXRhLCB3ZSBjYW4gYWRkIGxheWVycyBvbiB0byBpdCBhbmQgYWRqdXN0IGl0LgpXZSBjYW4gYWRqdXN0IHRoZSBjb2xvciBvZiBwb2ludHMgdXNpbmcgdGhlIGBjb2xvcmAgYWVzdGhldGljLgoKYGBge3IgZ2dwbG90LWNvbG9yLCBsaXZlID0gVFJVRX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkgIyBXZSBhZGRlZCB0aGlzIGFyZ3VtZW50IHRvIGNvbG9yIGNvZGUgdGhlIHBvaW50cyEKKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKQmVjYXVzZSB3ZSBoYXZlIHNvIG1hbnkgcG9pbnRzIG92ZXJsYXBwaW5nIG9uZSBhbm90aGVyLCB3ZSB3aWxsIHdhbnQgdG8gYWRqdXN0CnRoZSB0cmFuc3BhcmVuY3ksIHdoaWNoIHdlIGNhbiBkbyB3aXRoIGFuIGBhbHBoYWAgYXJndW1lbnQuCgpgYGB7ciBnZ3Bsb3QtYWxwaGEsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSAjIFdlIGFyZSB1c2luZyB0aGUgYGFscGhhYCBhcmd1bWVudCB0byBtYWtlIG91ciBwb2ludHMgdHJhbnNwYXJlbnQKYGBgCgpOb3RpY2UgdGhhdCB3ZSBhZGRlZCB0aGUgYWxwaGEgd2l0aGluIHRoZSBgZ2VvbV9wb2ludCgpYCBmdW5jdGlvbiwgbm90IHRvIHRoZSBgYWVzKClgLgpXZSBkaWQgdGhpcyBiZWNhdXNlIHdlIHdhbnQgYWxsIG9mIHRoZSBwb2ludHMgdG8gaGF2ZSB0aGUgc2FtZSBsZXZlbCBvZiB0cmFuc3BhcmVuY3ksIGFuZCBpdCB3aWxsIG5vdCB2YXJ5IGRlcGVuZGluZyBvbiBhbnkgdmFyaWFibGUgaW4gdGhlIGRhdGEuCldlIGNhbiBhbHNvIGNoYW5nZSB0aGUgYmFja2dyb3VuZCBhbmQgYXBwZWFyYW5jZSBvZiB0aGUgcGxvdCBhcyBhIHdob2xlIGJ5IGFkZGluZyBhIGB0aGVtZWAuCgpgYGB7ciBnZ3Bsb3QtdGhlbWV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAjIEFkZCBvbiB0aGlzIHNldCBvZiBhcHBlYXJhbmNlIHByZXNldHMgdG8gbWFrZSBpdCBwcmV0dHkKICB0aGVtZV9idygpIApgYGAKCldlIGFyZSBub3QgbGltaXRlZCB0byBhIHNpbmdsZSBwbG90dGluZyBsYXllci4KRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnQgdG8gYWRkIGEgaG9yaXpvbnRhbCBsaW5lIHRvIGluZGljYXRlIGEgc2lnbmlmaWNhbmNlIGN1dG9mZiwgd2UgY2FuIGRvIHRoYXQgd2l0aCBgZ2VvbV9obGluZSgpYC4KRm9yIG5vdywgd2Ugd2lsbCBjaG9vc2UgdGhlIHZhbHVlIG9mIDUuNSAodGhhdCBpcyBjbG9zZSB0byBhIEJvbmZlcnJvbmkgY29ycmVjdGlvbikgYW5kIGFkZCB0aGF0IHRvIHRoZSBwbG90LgoKYGBge3IgZ2dwbG90LWhsaW5lLCBsaXZlID0gVFJVRX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgIyB3ZSBjYW4gc3BlY2lmeSBjb2xvcnMgYnkgbmFtZXMgaGVyZQpgYGAKCldlIGNhbiBjaGFuZ2UgdGhlIHggYW5kIHkgbGFiZWxzIHVzaW5nIGEgZmV3IGRpZmZlcmVudCBzdHJhdGVnaWVzLgpPbmUgYXBwcm9hY2ggaXMgdG8gdXNlIGZ1bmN0aW9ucyBgeGxhYigpYCBhbmQgYHlsYWIoKWAgaW5kaXZpZHVhbGx5IHRvIHNldCwgcmVzcGVjdGl2ZWx5LCB0aGUgeC1heGlzIGxhYmVsIGFuZCB0aGUgdGhlIHktYXhpcyBsYWJlbC4KCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtMX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogICMgQWRkIGxhYmVscyB3aXRoIHNlcGFyYXRlIGZ1bmN0aW9uczoKICB4bGFiKCJsb2cyIEZvbGQgQ2hhbmdlIFR1bW9yL05vcm1hbCIpICsKICB5bGFiKCItbG9nMTAgcCB2YWx1ZSIpCmBgYAoKCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgdGhlIGBnZ3Bsb3QyYCBmdW5jdGlvbiBgbGFicygpYCwgd2hpY2ggdGFrZXMgaW5kaXZpZHVhbCBhcmd1bWVudHMgZm9yIGVhY2ggbGFiZWwgd2Ugd2FudCB3YW50IHRvIHNldC4KV2UgY2FuIGFsc28gaW5jbHVkZSB0aGUgYXJndW1lbnQgYHRpdGxlYCB0byBhZGQgYW4gb3ZlcmFsbCBwbG90IHRpdGxlLgoKYGBge3IgZ2dwbG90LWxhYmVsLTIsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgeCBhbmQgeSBsYWJlbHMgYW5kIG92ZXJhbGwgcGxvdCB0aXRsZSB3aXRoIGFyZ3VtZW50cyB0byBsYWJzKCk6CiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBUdW1vci9Ob3JtYWwiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICB0aXRsZSA9ICJBc3Ryb2N5dG9tYSBUdW1vciB2cyBOb3JtYWwgQ2VyZWJlbGx1bSIKICApCgpgYGAKClNvbWV0aGluZyBncmVhdCBhYm91dCB0aGUgYGxhYnMoKWAgZnVuY3Rpb24gaXMgeW91IGNhbiBhbHNvIHVzZSBpdCB0byBzcGVjaWZ5IGxhYmVscyBmb3IgeW91ciAqbGVnZW5kcyogZGVyaXZlZCBmcm9tIGNlcnRhaW4gYWVzdGhldGljcy4KSW4gdGhpcyBwbG90LCBvdXIgbGVnZW5kIGlzIGRlcml2ZWQgZnJvbSBhICpjb2xvciBhZXN0aGV0aWMqLCBzbyB3ZSBjYW4gc3BlY2lmeSB0aGUga2V5d29yZCAiY29sb3IiIHRvIHVwZGF0ZSB0aGUgbGVnZW5kIHRpdGxlLgoKYGBge3IgZ2dwbG90LWxhYmVsLWFlc30KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogICMgQWRkIHggYW5kIHkgbGFiZWxzIGFuZCBvdmVyYWxsIHBsb3QgdGl0bGUgKGFuZCBtb3JlISkgd2l0aCBhcmd1bWVudHMgdG8gbGFicygpOgogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UgVHVtb3IvTm9ybWFsIiwKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgdGl0bGUgPSAiQXN0cm9jeXRvbWEgVHVtb3IgdnMgTm9ybWFsIENlcmViZWxsdW0iLAogICAgIyBVc2UgdGhlIGNvbG9yIGtleXdvcmQgdG8gbGFiZWwgdGhlIGNvbG9yIGxlZ2VuZAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkKCmBgYAoKClVzZSB0aGlzIGNodW5rIHRvIG1ha2UgdGhlIHNhbWUga2luZCBvZiBwbG90IGFzIHRoZSBwcmV2aW91cyBjaHVuayBidXQgaW5zdGVhZCBwbG90IHRoZSBtYWxlIGZlbWFsZSBjb250cmFzdCBkYXRhLCB0aGF0IGlzIHN0b3JlZCBpbiBgbWFsZV9mZW1hbGVfZGZgLgoKYGBge3IgbWYtdm9sY2FubywgbGl2ZSA9IFRSVUV9CiMgVXNlIHRoaXMgY2h1bmsgdG8gbWFrZSB0aGUgc2FtZSBraW5kIG9mIHZvbGNhbm8gcGxvdCwgYnV0IHdpdGggdGhlIG1hbGUtZmVtYWxlIGNvbnRyYXN0IGRhdGEuCmdncGxvdCgKICBtYWxlX2ZlbWFsZV9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UgTWFsZS9GZW1hbGUiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKQpgYGAKCgpUdXJucyBvdXQsIHdlIGRvbid0IGhhdmUgdG8gcGxvdCBlYWNoIGNvbnRyYXN0IHNlcGFyYXRlbHksIGluc3RlYWQsIHdlIGNhbiB1c2UgdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyBhbGwgdGhyZWUgY29udHJhc3RzJyBkYXRhLCBgc3RhdHNfZGZgLCBhbmQgYWRkIGEgYGZhY2V0X3dyYXBgIHRvIG1ha2UgZWFjaCBjb250cmFzdCBpdHMgb3duIHBsb3QuCgpgYGB7ciBnZ3Bsb3QtZmFjZXRzfQpnZ3Bsb3QoCiAgc3RhdHNfZGYsICMgU3dpdGNoIHRvIHRoZSBiaWdnZXIgZGF0YSBmcmFtZSB3aXRoIGFsbCB0aHJlZSBjb250cmFzdHMnIGRhdGEKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGZhY2V0X3dyYXAodmFycyhjb250cmFzdCkpICsKICBsYWJzKAogICAgIyBOb3cgdGhhdCB0aGlzIGluY2x1ZGVzIHRoZSBvdGhlciBjb250cmFzdHMsCiAgICAjIHdlJ2xsIG1ha2UgdGhlIHgtYXhpcyBsYWJlbCBtb3JlIGdlbmVyYWwKICAgIHggID0gImxvZzIgRm9sZCBDaGFuZ2UiLCAKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMjUsIDI1KSkgIyB6b29tIGluIG9uIHRoZSB4LWF4aXMKYGBgCgpXZSBjYW4gc3RvcmUgdGhlIHBsb3QgYXMgYW4gb2JqZWN0IGluIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQgYnkgdXNpbmcgYDwtYCBvcGVyYXRvci4KSGVyZSB3ZSB3aWxsIGNhbGwgdGhpcyBgdm9sY2Fub19wbG90YC4KCmBgYHtyIGdncGxvdC1zdG9yZS1vYmplY3R9CiMgV2UgYXJlIHNhdmluZyB0aGlzIHBsb3QgdG8gYSB2YXJpYWJsZSBuYW1lZCBgdm9sY2Fub19wbG90YAp2b2xjYW5vX3Bsb3QgPC0gZ2dwbG90KAogIHN0YXRzX2RmLCAKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGZhY2V0X3dyYXAodmFycyhjb250cmFzdCkpICsKICBsYWJzKAogICAgeCA9ICJsb2cyIEZvbGQgQ2hhbmdlIiwKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMjUsIDI1KSkKYGBgCgpXaGVuIHdlIGFyZSBoYXBweSB3aXRoIG91ciBwbG90LCB3ZSBjYW4gc2F2ZSB0aGUgcGxvdCB1c2luZyBgZ2dzYXZlYC4KSXQncyBhIGdvb2QgaWRlYSB0byBhbHNvIHNwZWNpZnkgYHdpZHRoYCBhbmQgYGhlaWdodGAgYXJndW1lbnRzICh1bml0cyBpbiBpbmNoZXMpCnRvIGVuc3VyZSB0aGUgc2F2ZWQgcGxvdCBpcyBhbHdheXMgdGhlIHNhbWUgc2l6ZSBldmVyeSB0aW1lIHlvdSBydW4gdGhpcyBjb2RlLgpIZXJlLCB3ZSdsbCBzYXZlIGEgNiJ4NiIgcGxvdC4KCgpgYGB7ciBnZ3NhdmV9Cmdnc2F2ZSgKICBwbG90ID0gdm9sY2Fub19wbG90LAogIGZpbGVuYW1lID0gZmlsZS5wYXRoKHBsb3RzX2RpciwgInZvbGNhbm9fcGxvdC5wbmciKSwKICB3aWR0aCA9IDYsCiAgaGVpZ2h0ID0gNgopCmBgYAoKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CiMgUHJpbnQgb3V0IHRoZSB2ZXJzaW9ucyBhbmQgcGFja2FnZXMgd2UgYXJlIHVzaW5nIGluIHRoaXMgc2Vzc2lvbgpzZXNzaW9uSW5mbygpCmBgYAo=
    +
    LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdDIiCmF1dGhvcjogIkNDREwgZm9yIEFMU0YiCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBMb2FkIGFuZCB1c2UgUiBwYWNrYWdlcwotIFJlYWQgaW4gYW5kIHBlcmZvcm0gc2ltcGxlIG1hbmlwdWxhdGlvbnMgb2YgZGF0YSBmcmFtZXMKLSBVc2UgYGdncGxvdDJgIHRvIHBsb3QgYW5kIHZpc3VhbGl6ZSBkYXRhCi0gQ3VzdG9taXplIHBsb3RzIHVzaW5nIGZlYXR1cmVzIG9mIGBnZ3Bsb3QyYAoKLS0tCgpXZSdsbCB1c2UgYSByZWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHRvIGdldCBjb21mb3J0YWJsZSBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdXNpbmcgZ2dwbG90Mi4KV2UndmUgW3BlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlc10oLi9zY3JpcHRzLzAwLXNldHVwLWludHJvLXRvLVIuUikgb24gYSBwcmUtcHJvY2Vzc2VkIFthc3Ryb2N5dG9tYSBtaWNyb2FycmF5IGRhdGFzZXRdKGh0dHBzOi8vd3d3LnJlZmluZS5iaW8vZXhwZXJpbWVudHMvR1NFNDQ5NzEvZ2VuZS1leHByZXNzaW9uLWRhdGEtZnJvbS1waWxvY3l0aWMtYXN0cm9jeXRvbWEtdHVtb3VyLXNhbXBsZXMtYW5kLW5vcm1hbC1jZXJlYmVsbHVtLWNvbnRyb2xzKS4KV2UnbGwgc3RhcnQgYnkgbWFraW5nIGEgdm9sY2FubyBwbG90IG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBmcm9tIHRoaXMgZXhwZXJpbWVudC4KV2UgcGVyZm9ybWVkIHRocmVlIHNldHMgb2YgY29udHJhc3RzOgoKMSkgYHNleGAgY2F0ZWdvcnkgY29udHJhc3Rpbmc6IGBNYWxlYCB2cyBgRmVtYWxlYAoyKSBgdGlzc3VlYCBjYXRlZ29yeSBjb250cmFzdGluZyA6IGBQaWxvY3l0aWMgYXN0cm9jeXRvbWEgdHVtb3JgIHNhbXBsZXMgdnMgYG5vcm1hbCBjZXJlYmVsbHVtYCBzYW1wbGVzCjMpIEFuIGludGVyYWN0aW9uIG9mIGJvdGggYHNleGAgYW5kIGB0aXNzdWVgLgoKKipNb3JlIGdncGxvdDIgcmVzb3VyY2VzOioqCgotIFtnZ3Bsb3QyIHdlYnNpdGVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykKLSBbSGFuZHkgY2hlYXRzaGVldCBmb3IgZ2dwbG90MiAocGRmKV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS12aXN1YWxpemF0aW9uLnBkZikKLSBbX0RhdGEgVmlzdWFsaXphdGlvbiwgQSBwcmFjdGljYWwgaW50cm9kdWN0aW9uX10oaHR0cHM6Ly9zb2N2aXouY28vKQotIFtEYXRhIHZpc3VhbGl6YXRpb24gY2hhcHRlciBvZiBfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZGxleS5uei9kYXRhLXZpc3VhbGl6ZS5odG1sKQotIFtnZ3Bsb3QyIG9ubGluZSB0dXRvcmlhbF0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWwpCgojIyBTZXQgVXAKCldlIHNhdmVkIHRoZXNlIHJlc3VsdHMgdG8gYSB0YWIgc2VwYXJhdGVkIHZhbHVlcyAoVFNWKSBmaWxlIGNhbGxlZCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAuCkl0J3MgYmVlbiBzYXZlZCB0byB0aGUgYGRhdGFgIGZvbGRlci4KRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gd2hlcmUgdGhpcyBub3RlYm9vayBmaWxlICguUm1kKSBpcyBzYXZlZC4KU28gd2UgY2FuIHJlZmVyZW5jZSBpdCBsYXRlciwgbGV0J3MgbWFrZSBhIHZhcmlhYmxlIHdpdGggb3VyIGRhdGEgZGlyZWN0b3J5IG5hbWUuCgpgYGB7cn0KZGF0YV9kaXIgPC0gImRhdGEiCmBgYAoKTGV0J3MgZGVjbGFyZSBvdXIgb3V0cHV0IGZvbGRlciBuYW1lIGFzIGl0cyBvd24gdmFyaWFibGUuCgpgYGB7cn0KcGxvdHNfZGlyIDwtICJwbG90cyIKYGBgCgpXZSBjYW4gYWxzbyBjcmVhdGUgYSBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0LgoKVGhlcmUncyBhIGNvdXBsZSB3YXlzIHRoYXQgd2UgY2FuIGNyZWF0ZSB0aGF0IGRpcmVjdG9yeSBmcm9tIHdpdGhpbiBSLgpPbmUgd2F5IGlzIHRvIHVzZSB0aGUgYmFzZSBSIGZ1bmN0aW9uIGBkaXIuY3JlYXRlKClgLCB3aGljaCAoYXMgdGhlIG5hbWUgc3VnZ2VzdHMpIHdpbGwgY3JlYXRlIGEgZGlyZWN0b3J5LgpCdXQgdGhpcyBmdW5jdGlvbiBhc3N1bWVzIHRoYXQgdGhlIGRpcmVjdG9yeSBkb2VzIG5vdCB5ZXQgZXhpc3QsIGFuZCBpdCB3aWxsIHRocm93IGFuIGVycm9yIGlmIHlvdSB0cnkgdG8gY3JlYXRlIGEgZGlyZWN0b3J5IHRoYXQgYWxyZWFkeSBleGlzdHMuClRvIGF2b2lkIHRoaXMgZXJyb3IsIHdlIGNhbiBwbGFjZSB0aGUgZGlyZWN0b3J5IGNyZWF0aW9uIGluc2lkZSBhbiBgaWZgIHN0YXRlbWVudCwgc28gdGhlIGNvZGUgd2lsbCBvbmx5IHJ1biBpZiB0aGUgZGlyZWN0b3J5IGRvZXMgbm90IHlldCBleGlzdDoKCmBgYHtyIGNyZWF0ZWlmfQojIFRoZSBpZiBzdGF0ZW1lbnQgaGVyZSB0ZXN0cyB3aGV0aGVyIHRoZSBwbG90IGRpcmVjdG9yeSBleGlzdHMgYW5kCiMgb25seSBleGVjdXRlcyB0aGUgZXhwcmVzc2lvbnMgYmV0d2VlbiB0aGUgYnJhY2VzIGlmIGl0IGRvZXMgbm90LgppZiAoIWRpci5leGlzdHMocGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUocGxvdHNfZGlyKQp9CmBgYAoKSW4gdGhpcyBub3RlYm9vayB3ZSB3aWxsIGJlIHVzaW5nIGZ1bmN0aW9ucyBmcm9tIHRoZSBUaWR5dmVyc2Ugc2V0IG9mIHBhY2thZ2VzLCBzbyB3ZSBuZWVkIHRvIGxvYWQgaW4gdGhvc2UgZnVuY3Rpb25zIHVzaW5nIGBsaWJyYXJ5KClgLgpXZSBjb3VsZCBsb2FkIHRoZSBpbmRpdmlkdWFsIHBhY2thZ2VzIHdlIG5lZWQgb25lIGF0IGEgdGltZSwgYnV0IGl0IGlzIGNvbnZlbmllbnQgZm9yIG5vdyB0byBsb2FkIHRoZW0gYWxsIHdpdGggdGhlIGB0aWR5dmVyc2VgICJwYWNrYWdlLCIgd2hpY2ggZ3JvdXBzIG1hbnkgb2YgdGhlbSB0b2dldGhlciBhcyBhIHNob3J0Y3V0LgpLZWVwIGEgbG9vayBvdXQgZm9yIHdoZXJlIHdlIHRlbGwgeW91IHdoaWNoIGluZGl2aWR1YWwgcGFja2FnZSBkaWZmZXJlbnQgZnVuY3Rpb25zIGNvbWUgZnJvbS4KCmBgYHtyIHRpZHl2ZXJzZX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgUmVhZCBpbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0cyBmaWxlCgpIZXJlIHdlIGFyZSB1c2luZyBhIGB0aWR5dmVyc2VgIGZ1bmN0aW9uIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkxpa2Ugd2UgZGlkIGluIHRoZSBwcmV2aW91cyBub3RlYm9vaywgd2Ugd2lsbCBzdG9yZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgYXMgYHN0YXRzX2RmYC4KCmBgYHtyIHJlYWQtc3RhdHN9CiMgcmVhZCBpbiB0aGUgZmlsZSBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAgZnJvbSB0aGUgZGF0YSBkaXJlY3RvcnkKc3RhdHNfZGYgPC0gcmVhZF90c3YoZmlsZS5wYXRoKAogIGRhdGFfZGlyLAogICJnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2IgopKQpgYGAKCldlIGNhbiB0YWtlIGEgbG9vayBhdCBhIGNvbHVtbiBpbmRpdmlkdWFsbHkgYnkgdXNpbmcgYSBgJGAuCk5vdGUgd2UgYXJlIHVzaW5nIGBoZWFkKClgIHNvIHRoZSB3aG9sZSB0aGluZyBkb2Vzbid0IHByaW50IG91dC4KCmBgYHtyIGNvbHVtbn0KaGVhZChzdGF0c19kZiRjb250cmFzdCkKYGBgCgpJZiB3ZSB3YW50IHRvIHNlZSBhIHNwZWNpZmljIHNldCBvZiB2YWx1ZXMsIHdlIGNhbiB1c2UgYnJhY2tldHMgd2l0aCB0aGUgaW5kaWNlcyBvZiB0aGUgdmFsdWVzIHdlJ2QgbGlrZSByZXR1cm5lZC4KCmBgYHtyfQpzdGF0c19kZiRhdmdfZXhwcmVzc2lvbls2OjEwXQpgYGAKCkxldCdzIGxvb2sgYXQgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGZyb20gdGhlIGRhdGEgc2V0IHVzaW5nIGBzdW1tYXJ5KClgCgpgYGB7ciBzdGF0cy1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzdW1tYXJ5IG9mIHN0YXRzX2RmCnN1bW1hcnkoc3RhdHNfZGYpCmBgYAoKVGhlIHN0YXRpc3RpY3MgZm9yIGBjb250cmFzdGAgYXJlIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLCBzbyBsZXQncyBkbyB0aGF0IGFnYWluIHdpdGgganVzdCB0aGUgYGNvbnRyYXN0YCBjb2x1bW4gYWZ0ZXIgY29udmVydGluZyBpdCB0byBhIGBmYWN0b3JgCmBgYHtyIGZhY3Rvci1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzdW1tYXJ5IG9mIGBzdGF0c19kZiRjb250cmFzdGAgYXMgYSBmYWN0b3IKc3VtbWFyeShhcy5mYWN0b3Ioc3RhdHNfZGYkY29udHJhc3QpKQpgYGAKCiMjIFNldCB1cCB0aGUgZGF0YXNldAoKQmVmb3JlIHdlIG1ha2Ugb3VyIHBsb3QsIHdlIHdhbnQgdG8gY2FsY3VsYXRlIGEgc2V0IG9mIG5ldyB2YWx1ZXMgZm9yIGVhY2ggcm93OyB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIHJhdyBzdGF0aXN0aWNzIGluIG91ciB0YWJsZS4KVG8gZG8gdGhpcyB3ZSB3aWxsIHVzZSBhIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSBjYWxsZWQgYG11dGF0ZSgpYCB0byBtYWtlIGEgbmV3IGNvbHVtbiBvZiAtbG9nMTAgcCB2YWx1ZXMuCgpgYGB7ciBtdXRhdGV9CiMgYWRkIGEgYG5lZ19sb2cxMF9wYCBjb2x1bW4gdG8gdGhlIGRhdGEgZnJhbWUKc3RhdHNfZGYgPC0gbXV0YXRlKHN0YXRzX2RmLCAjIGRhdGEgZnJhbWUgd2UnZCBsaWtlIHRvIGFkZCBhIHZhcmlhYmxlIHRvCiAgbmVnX2xvZzEwX3AgPSAtbG9nMTAocF92YWx1ZSkgIyBjb2x1bW4gbmFtZSBhbmQgdmFsdWVzCikKYGBgCgpMZXQncyBmaWx0ZXIgdG8gb25seSBgbWFsZV9mZW1hbGVgIGNvbnRyYXN0IGRhdGEuCkZpcnN0IGxldCdzIHRyeSBvdXQgYSBsb2dpY2FsIGV4cHJlc3Npb246CgpgYGB7ciBldmFsID0gRkFMU0V9CnN0YXRzX2RmJGNvbnRyYXN0ID09ICJtYWxlX2ZlbWFsZSIKYGBgCgpOb3cgd2UgY2FuIHRyeSBvdXQgdGhlIGBmaWx0ZXIoKWAgZnVuY3Rpb24uCk5vdGljZSB0aGF0IHdlIGFyZSBub3QgYXNzaWduaW5nIHRoZSByZXN1bHRzIHRvIGEgdmFyaWFibGUsIHNvIHRoaXMgZmlsdGVyZWQgZGF0YXNldCB3aWxsIG5vdCBiZSBzYXZlZCB0byB0aGUgZW52aXJvbm1lbnQuCgpgYGB7ciBmaWx0ZXIsIGxpdmUgPSBUUlVFfQojIGZpbHRlciBzdGF0c19kZiB0byAibWFsZV9mZW1hbGUiIG9ubHkKZmlsdGVyKHN0YXRzX2RmLCBjb250cmFzdCA9PSAibWFsZV9mZW1hbGUiKQpgYGAKCk5vdyB3ZSBjYW4gYXNzaWduIHRoZSByZXN1bHRzIHRvIGEgbmV3IGRhdGEgZnJhbWU6IGBtYWxlX2ZlbWFsZV9kZmAuCgpgYGB7ciBmaWx0ZXItc2F2ZSwgbGl2ZSA9IFRSVUV9CiMgZmlsdGVyIGFuZCBzYXZlIHRvIG1hbGVfZmVtYWxlX2RmCm1hbGVfZmVtYWxlX2RmIDwtIGZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIikKYGBgCgojIyBQbG90dGluZyB0aGlzIGRhdGEKCkxldCdzIG1ha2UgYSB2b2xjYW5vIHBsb3Qgd2l0aCB0aGlzIGRhdGEuCkZpcnN0IGxldCdzIHRha2UgYSBsb29rIGF0IG9ubHkgdGhlIHR1bW9yIHZzLiBub3JtYWwgY29tcGFyaXNvbi4KTGV0J3Mgc2F2ZSB0aGlzIGFzIGEgc2VwYXJhdGUgZGF0YSBmcmFtZSBieSBhc3NpZ25pbmcgaXQgYSBuZXcgbmFtZS4KCmBgYHtyIGZpbHRlci10dW1vcn0KdHVtb3Jfbm9ybWFsX2RmIDwtIGZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gImFzdHJvY3l0b21hX25vcm1hbCIpCmBgYAoKVG8gbWFrZSB0aGlzIHBsb3Qgd2Ugd2lsbCBiZSB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgYGdncGxvdDJgIHBhY2thZ2UsIHRoZSBtYWluIHBsb3R0aW5nIHBhY2thZ2Ugb2YgdGhlIHRpZHl2ZXJzZS4KV2UgdXNlIHRoZSBmaXJzdCBmdW5jdGlvbiwgYGdncGxvdCgpYCB0byBkZWZpbmUgdGhlIGRhdGEgdGhhdCB3aWxsIGJlIHBsb3R0ZWQuClJlbWVtYmVyLCB0aGUgbmFtZSBvZiB0aGlzIHBhY2thZ2UgaXMgYGdncGxvdDJgLCBidXQgdGhlIGZ1bmN0aW9uIHdlIHVzZSBpcyBjYWxsZWQgYGdncGxvdCgpYCB3aXRob3V0IHRoZSBgMmAuCmBnZ3Bsb3QoKWAgdGFrZXMgdHdvIG1haW4gYXJndW1lbnRzOgoKMS4gYGRhdGFgLCB3aGljaCBpcyB0aGUgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdC4KMi4gYG1hcHBpbmdgLCB3aGljaCBpcyBhIHNwZWNpYWwgbGlzdCBtYWRlIHdpdGggdGhlIGBhZXMoKWAgZnVuY3Rpb24gdG8gZGVzY3JpYmUgd2hpY2ggdmFsdWVzIHdpbGwgYmUgdXNlZCBmb3IgZWFjaCAqKmFlcyoqdGhldGljIGNvbXBvbmVudCBvZiB0aGUgcGxvdCwgc3VjaCBhcyB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyBvZiBlYWNoIHBvaW50LgooSWYgeW91IGZpbmQgY2FsbGluZyB0aGluZ3MgbGlrZSB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyAiYWVzdGhldGljcyIgY29uZnVzaW5nLCBkb24ndCB3b3JyeSwgeW91IGFyZSBub3QgYWxvbmUuKQpTcGVjaWZpY2FsbHksIHRoZSBgYWVzKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gc3BlY2lmeSB0aGF0IGEgZ2l2ZW4gY29sdW1uICh2YXJpYWJsZSkgaW4geW91ciBkYXRhIGZyYW1lIGJlIG1hcHBlZCB0byBhIGdpdmVuIGFlc3RoZXRpYyBjb21wb25lbnQgb2YgdGhlIHBsb3QuCgoKYGBge3IgZ2dwbG90LWJhc2V9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsICMgVGhpcyBmaXJzdCBhcmd1bWVudCBpcyB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsICMgVGhpcyBpcyB0aGUgY29sdW1uIG5hbWUgb2YgdGhlIHZhbHVlcyB3ZSB3YW50IHRvIHVzZQogICAgIyBmb3IgdGhlIHggY29vcmRpbmF0ZXMKICAgIHkgPSBuZWdfbG9nMTBfcAogICkgIyBUaGlzIGlzIHRoZSBjb2x1bW4gbmFtZSBvZiB0aGUgZGF0YSB3ZSB3YW50IGZvciB0aGUgeS1heGlzCikKYGBgCgpZb3UnbGwgbm90aWNlIHRoaXMgcGxvdCBkb2Vzbid0IGhhdmUgYW55dGhpbmcgb24gaXQgYmVjYXVzZSB3ZSBoYXZlbid0CnNwZWNpZmllZCBhIHBsb3QgdHlwZSB5ZXQuClRvIGRvIHRoYXQsIHdlIHdpbGwgYWRkIGFub3RoZXIgZ2dwbG90IGxheWVyIHdpdGggYCtgIHdoaWNoIHdpbGwgc3BlY2lmeSBleGFjdGx5IHdoYXQgd2Ugd2FudCB0byBwbG90LgpBIHZvbGNhbm8gcGxvdCBpcyBhIHNwZWNpYWwga2luZCBvZiBzY2F0dGVyIHBsb3QsIHNvIHRvIG1ha2UgdGhhdCB3ZSB3aWxsIHdhbnQgdG8gcGxvdCBpbmRpdmlkdWFsIHBvaW50cywgd2hpY2ggd2UgY2FuIGRvIHdpdGggYGdlb21fcG9pbnQoKWAuCgpgYGB7ciBnZ3Bsb3QtcG9pbnRzLCBsaXZlID0gVFJVRX0KIyBUaGlzIGZpcnN0IHBhcnQgaXMgdGhlIHNhbWUgYXMgYmVmb3JlCmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcAogICkKKSArCiAgIyBOb3cgd2UgYXJlIGFkZGluZyBvbiBhIGxheWVyIHRvIHNwZWNpZnkgd2hhdCBraW5kIG9mIHBsb3Qgd2Ugd2FudAogIGdlb21fcG9pbnQoKQpgYGAKCkhlcmUncyBhIGJyaWVmIHN1bW1hcnkgb2YgZ2dwbG90MiBzdHJ1Y3R1cmUuCiFbZ2dwbG90MiBzdHJ1Y3R1cmVdKGRpYWdyYW1zL2dncGxvdF9zdHJ1Y3R1cmUucG5nKQoKIyMjIEFkanVzdCBvdXIgZ2dwbG90CgpOb3cgdGhhdCB3ZSBoYXZlIGEgYmFzZSBwbG90IHRoYXQgc2hvd3Mgb3VyIGRhdGEsIHdlIGNhbiBhZGQgbGF5ZXJzIG9uIHRvIGl0IGFuZCBhZGp1c3QgaXQuCldlIGNhbiBhZGp1c3QgdGhlIGNvbG9yIG9mIHBvaW50cyB1c2luZyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMuCgpgYGB7ciBnZ3Bsb3QtY29sb3IsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKSAjIFdlIGFkZGVkIHRoaXMgYXJndW1lbnQgdG8gY29sb3IgY29kZSB0aGUgcG9pbnRzIQopICsKICBnZW9tX3BvaW50KCkKYGBgCgpCZWNhdXNlIHdlIGhhdmUgc28gbWFueSBwb2ludHMgb3ZlcmxhcHBpbmcgb25lIGFub3RoZXIsIHdlIHdpbGwgd2FudCB0byBhZGp1c3QKdGhlIHRyYW5zcGFyZW5jeSwgd2hpY2ggd2UgY2FuIGRvIHdpdGggYW4gYGFscGhhYCBhcmd1bWVudC4KCmBgYHtyIGdncGxvdC1hbHBoYSwgbGl2ZSA9IFRSVUV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICMgV2UgYXJlIHVzaW5nIHRoZSBgYWxwaGFgIGFyZ3VtZW50IHRvIG1ha2Ugb3VyIHBvaW50cyB0cmFuc3BhcmVudApgYGAKCk5vdGljZSB0aGF0IHdlIGFkZGVkIHRoZSBhbHBoYSB3aXRoaW4gdGhlIGBnZW9tX3BvaW50KClgIGZ1bmN0aW9uLCBub3QgdG8gdGhlIGBhZXMoKWAuCldlIGRpZCB0aGlzIGJlY2F1c2Ugd2Ugd2FudCBhbGwgb2YgdGhlIHBvaW50cyB0byBoYXZlIHRoZSBzYW1lIGxldmVsIG9mIHRyYW5zcGFyZW5jeSwgYW5kIGl0IHdpbGwgbm90IHZhcnkgZGVwZW5kaW5nIG9uIGFueSB2YXJpYWJsZSBpbiB0aGUgZGF0YS4KV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBiYWNrZ3JvdW5kIGFuZCBhcHBlYXJhbmNlIG9mIHRoZSBwbG90IGFzIGEgd2hvbGUgYnkgYWRkaW5nIGEgYHRoZW1lYC4KCmBgYHtyIGdncGxvdC10aGVtZX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICMgQWRkIG9uIHRoaXMgc2V0IG9mIGFwcGVhcmFuY2UgcHJlc2V0cyB0byBtYWtlIGl0IHByZXR0eQogIHRoZW1lX2J3KCkgCmBgYAoKV2UgYXJlIG5vdCBsaW1pdGVkIHRvIGEgc2luZ2xlIHBsb3R0aW5nIGxheWVyLgpGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudCB0byBhZGQgYSBob3Jpem9udGFsIGxpbmUgdG8gaW5kaWNhdGUgYSBzaWduaWZpY2FuY2UgY3V0b2ZmLCB3ZSBjYW4gZG8gdGhhdCB3aXRoIGBnZW9tX2hsaW5lKClgLgpGb3Igbm93LCB3ZSB3aWxsIGNob29zZSB0aGUgdmFsdWUgb2YgNS41ICh0aGF0IGlzIGNsb3NlIHRvIGEgQm9uZmVycm9uaSBjb3JyZWN0aW9uKSBhbmQgYWRkIHRoYXQgdG8gdGhlIHBsb3QuCgpgYGB7ciBnZ3Bsb3QtaGxpbmUsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSAjIHdlIGNhbiBzcGVjaWZ5IGNvbG9ycyBieSBuYW1lcyBoZXJlCmBgYAoKV2UgY2FuIGNoYW5nZSB0aGUgeCBhbmQgeSBsYWJlbHMgdXNpbmcgYSBmZXcgZGlmZmVyZW50IHN0cmF0ZWdpZXMuCk9uZSBhcHByb2FjaCBpcyB0byB1c2UgZnVuY3Rpb25zIGB4bGFiKClgIGFuZCBgeWxhYigpYCBpbmRpdmlkdWFsbHkgdG8gc2V0LCByZXNwZWN0aXZlbHksIHRoZSB4LWF4aXMgbGFiZWwgYW5kIHRoZSB0aGUgeS1heGlzIGxhYmVsLgoKCmBgYHtyIGdncGxvdC1sYWJlbC0xfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgbGFiZWxzIHdpdGggc2VwYXJhdGUgZnVuY3Rpb25zOgogIHhsYWIoImxvZzIgRm9sZCBDaGFuZ2UgVHVtb3IvTm9ybWFsIikgKwogIHlsYWIoIi1sb2cxMCBwIHZhbHVlIikKYGBgCgoKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSB0aGUgYGdncGxvdDJgIGZ1bmN0aW9uIGBsYWJzKClgLCB3aGljaCB0YWtlcyBpbmRpdmlkdWFsIGFyZ3VtZW50cyBmb3IgZWFjaCBsYWJlbCB3ZSB3YW50IHdhbnQgdG8gc2V0LgpXZSBjYW4gYWxzbyBpbmNsdWRlIHRoZSBhcmd1bWVudCBgdGl0bGVgIHRvIGFkZCBhbiBvdmVyYWxsIHBsb3QgdGl0bGUuCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtMiwgbGl2ZSA9IFRSVUV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LjUsIGNvbG9yID0gImRhcmtncmVlbiIpICsKICB0aGVtZV9idygpICsKICAjIEFkZCB4IGFuZCB5IGxhYmVscyBhbmQgb3ZlcmFsbCBwbG90IHRpdGxlIHdpdGggYXJndW1lbnRzIHRvIGxhYnMoKToKICBsYWJzKAogICAgeCA9ICJsb2cyIEZvbGQgQ2hhbmdlIFR1bW9yL05vcm1hbCIsCiAgICB5ID0gIi1sb2cxMCBwIHZhbHVlIiwKICAgIHRpdGxlID0gIkFzdHJvY3l0b21hIFR1bW9yIHZzIE5vcm1hbCBDZXJlYmVsbHVtIgogICkKCmBgYAoKU29tZXRoaW5nIGdyZWF0IGFib3V0IHRoZSBgbGFicygpYCBmdW5jdGlvbiBpcyB5b3UgY2FuIGFsc28gdXNlIGl0IHRvIHNwZWNpZnkgbGFiZWxzIGZvciB5b3VyICpsZWdlbmRzKiBkZXJpdmVkIGZyb20gY2VydGFpbiBhZXN0aGV0aWNzLgpJbiB0aGlzIHBsb3QsIG91ciBsZWdlbmQgaXMgZGVyaXZlZCBmcm9tIGEgKmNvbG9yIGFlc3RoZXRpYyosIHNvIHdlIGNhbiBzcGVjaWZ5IHRoZSBrZXl3b3JkICJjb2xvciIgdG8gdXBkYXRlIHRoZSBsZWdlbmQgdGl0bGUuCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtYWVzfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgeCBhbmQgeSBsYWJlbHMgYW5kIG92ZXJhbGwgcGxvdCB0aXRsZSAoYW5kIG1vcmUhKSB3aXRoIGFyZ3VtZW50cyB0byBsYWJzKCk6CiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBUdW1vci9Ob3JtYWwiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICB0aXRsZSA9ICJBc3Ryb2N5dG9tYSBUdW1vciB2cyBOb3JtYWwgQ2VyZWJlbGx1bSIsCiAgICAjIFVzZSB0aGUgY29sb3Iga2V5d29yZCB0byBsYWJlbCB0aGUgY29sb3IgbGVnZW5kCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKQoKYGBgCgoKVXNlIHRoaXMgY2h1bmsgdG8gbWFrZSB0aGUgc2FtZSBraW5kIG9mIHBsb3QgYXMgdGhlIHByZXZpb3VzIGNodW5rIGJ1dCBpbnN0ZWFkIHBsb3QgdGhlIG1hbGUgZmVtYWxlIGNvbnRyYXN0IGRhdGEsIHRoYXQgaXMgc3RvcmVkIGluIGBtYWxlX2ZlbWFsZV9kZmAuCgpgYGB7ciBtZi12b2xjYW5vLCBsaXZlID0gVFJVRX0KIyBVc2UgdGhpcyBjaHVuayB0byBtYWtlIHRoZSBzYW1lIGtpbmQgb2Ygdm9sY2FubyBwbG90LCBidXQgd2l0aCB0aGUgbWFsZS1mZW1hbGUgY29udHJhc3QgZGF0YS4KZ2dwbG90KAogIG1hbGVfZmVtYWxlX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBNYWxlL0ZlbWFsZSIsCiAgICB5ID0gIi1sb2cxMCBwIHZhbHVlIiwKICAgIGNvbG9yID0gIkF2ZXJhZ2UgZXhwcmVzc2lvbiIKICApCmBgYAoKClR1cm5zIG91dCwgd2UgZG9uJ3QgaGF2ZSB0byBwbG90IGVhY2ggY29udHJhc3Qgc2VwYXJhdGVseSwgaW5zdGVhZCwgd2UgY2FuIHVzZSB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIGFsbCB0aHJlZSBjb250cmFzdHMnIGRhdGEsIGBzdGF0c19kZmAsIGFuZCBhZGQgYSBgZmFjZXRfd3JhcGAgdG8gbWFrZSBlYWNoIGNvbnRyYXN0IGl0cyBvd24gcGxvdC4KCmBgYHtyIGdncGxvdC1mYWNldHN9CmdncGxvdCgKICBzdGF0c19kZiwgIyBTd2l0Y2ggdG8gdGhlIGJpZ2dlciBkYXRhIGZyYW1lIHdpdGggYWxsIHRocmVlIGNvbnRyYXN0cycgZGF0YQogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgZmFjZXRfd3JhcCh2YXJzKGNvbnRyYXN0KSkgKwogIGxhYnMoCiAgICAjIE5vdyB0aGF0IHRoaXMgaW5jbHVkZXMgdGhlIG90aGVyIGNvbnRyYXN0cywKICAgICMgd2UnbGwgbWFrZSB0aGUgeC1heGlzIGxhYmVsIG1vcmUgZ2VuZXJhbAogICAgeCAgPSAibG9nMiBGb2xkIENoYW5nZSIsIAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0yNSwgMjUpKSAjIHpvb20gaW4gb24gdGhlIHgtYXhpcwpgYGAKCldlIGNhbiBzdG9yZSB0aGUgcGxvdCBhcyBhbiBvYmplY3QgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudCBieSB1c2luZyBgPC1gIG9wZXJhdG9yLgpIZXJlIHdlIHdpbGwgY2FsbCB0aGlzIGB2b2xjYW5vX3Bsb3RgLgoKYGBge3IgZ2dwbG90LXN0b3JlLW9iamVjdH0KIyBXZSBhcmUgc2F2aW5nIHRoaXMgcGxvdCB0byBhIHZhcmlhYmxlIG5hbWVkIGB2b2xjYW5vX3Bsb3RgCnZvbGNhbm9fcGxvdCA8LSBnZ3Bsb3QoCiAgc3RhdHNfZGYsIAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgZmFjZXRfd3JhcCh2YXJzKGNvbnRyYXN0KSkgKwogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0yNSwgMjUpKQpgYGAKCldoZW4gd2UgYXJlIGhhcHB5IHdpdGggb3VyIHBsb3QsIHdlIGNhbiBzYXZlIHRoZSBwbG90IHVzaW5nIGBnZ3NhdmVgLgpJdCdzIGEgZ29vZCBpZGVhIHRvIGFsc28gc3BlY2lmeSBgd2lkdGhgIGFuZCBgaGVpZ2h0YCBhcmd1bWVudHMgKHVuaXRzIGluIGluY2hlcykKdG8gZW5zdXJlIHRoZSBzYXZlZCBwbG90IGlzIGFsd2F5cyB0aGUgc2FtZSBzaXplIGV2ZXJ5IHRpbWUgeW91IHJ1biB0aGlzIGNvZGUuCkhlcmUsIHdlJ2xsIHNhdmUgYSA2Ing2IiBwbG90LgoKCmBgYHtyIGdnc2F2ZX0KZ2dzYXZlKAogIHBsb3QgPSB2b2xjYW5vX3Bsb3QsCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgocGxvdHNfZGlyLCAidm9sY2Fub19wbG90LnBuZyIpLAogIHdpZHRoID0gNiwKICBoZWlnaHQgPSA2CikKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0KIyBQcmludCBvdXQgdGhlIHZlcnNpb25zIGFuZCBwYWNrYWdlcyB3ZSBhcmUgdXNpbmcgaW4gdGhpcyBzZXNzaW9uCnNlc3Npb25JbmZvKCkKYGBgCg==
    diff --git a/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd b/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd index eeb867ea..a0414c65 100644 --- a/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd +++ b/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd @@ -27,7 +27,7 @@ It is a pre-processed [astrocytoma microarray dataset](https://www.refine.bio/ex **More tidyverse resources:** -- [R for Data Science](https://r4ds.had.co.nz/) +- [R for Data Science](https://r4ds.hadley.nz/) - [tidyverse documentation](https://tidyverse.org/) - [`dplyr` documentation](https://dplyr.tidyverse.org/) - [`readr` documentation](https://readr.tidyverse.org/) @@ -175,7 +175,7 @@ What information is contained in `gene_df`? One nifty feature that was added to `R` in version 4.1 is the pipe: `|>`. Pipes are very handy things that allow you to funnel the result of one expression to the next, making your code more streamlined and fluently expressing the flow of data through a series of operations. -_Note:_ If you are using a version of `R` prior to 4.1 (or looking at older code), pipe functionality was available through the `magrittr` package , which used a pipe that looked like this: `%>%`. +_Note:_ If you are using a version of `R` prior to 4.1 (or looking at older code), pipe functionality was available through the `magrittr` package, which used a pipe that looked like this: `%>%`. That pipe was the inspiration for the native R pipe we are using here. While there are some minor differences, you can mostly treat them interchangeably as long as you load the `magrittr` package or `dplyr`, which also loads that version of the pipe. diff --git a/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html b/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html index b2459ee8..e04143a4 100644 --- a/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html +++ b/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html @@ -2903,7 +2903,7 @@

    Objectives

    on.

    More tidyverse resources:

    In this example, we will use canonical pathways which are (ref):

    -

    Gene sets from pathway databases. Usually, these gene sets are canonical representations of a biological process compiled by domain experts.

    +

    Gene sets from pathway databases. Usually, these gene sets are +canonical representations of a biological process compiled by domain +experts.

    -

    And are a subset of C2: curated gene sets. Specifically, we will use the KEGG (Kyoto Encyclopedia of Genes and Genomes) pathways.

    +

    And are a subset of C2: curated gene sets. Specifically, +we will use the KEGG (Kyoto +Encyclopedia of Genes and Genomes) pathways.

    - +
    # Filter the mouse data frame to the KEGG pathways that are included in the
     # curated gene sets
    -mm_kegg_df <- mm_msigdb_df %>%
    -  dplyr::filter(gs_cat == "C2",  # curated gene sets 
    -                gs_subcat == "CP:KEGG")  # KEGG pathways 
    +mm_kegg_df <- mm_msigdb_df |> + dplyr::filter(gs_cat == "C2", # curated gene sets + gs_subcat == "CP:KEGG") # KEGG pathways
    -

    Note: We could specified that we wanted the KEGG gene sets using the category and subcategory arguments of msigdbr(), but we’re going for general steps!

    +

    Note: We could specified that we wanted the KEGG gene sets using +the category and subcategory arguments of +msigdbr(), but we’re going for general steps!

    colnames(mm_kegg_df)
    - +
     [1] "gs_cat"               "gs_subcat"            "gs_name"             
    - [4] "entrez_gene"          "gene_symbol"          "human_entrez_gene"   
    - [7] "human_gene_symbol"    "gs_id"                "gs_pmid"             
    -[10] "gs_geoid"             "gs_exact_source"      "gs_url"              
    -[13] "gs_description"       "species_name"         "species_common_name" 
    -[16] "ortholog_sources"     "num_ortholog_sources"
    + [4] "gene_symbol" "entrez_gene" "ensembl_gene" + [7] "human_gene_symbol" "human_entrez_gene" "human_ensembl_gene" +[10] "gs_id" "gs_pmid" "gs_geoid" +[13] "gs_exact_source" "gs_url" "gs_description" +[16] "taxon_id" "ortholog_sources" "num_ortholog_sources"
    - +
    gs_cat
    -
     gs_subcat
    -
     gs_name
    -
    -entrez_gene
    -
     gene_symbol
    -
    -human_entrez_gene
    -
    +entrez_gene
    +ensembl_gene
     human_gene_symbol
    -
    +human_entrez_gene
    +human_ensembl_gene
     gs_id
    -
     gs_pmid
    -
     gs_geoid
    -
     gs_exact_source
    -
     gs_url
    -
     gs_description
    -
    -species_name
    -
    -species_common_name
    -
    +taxon_id
     ortholog_sources
    -
     num_ortholog_sources
    -

    The clusterProfiler function we will use requires a data frame with two columns, where one column contains the term identifier or name and one column contains gene identifiers that match our gene lists we want to check for enrichment. Our data frame with KEGG terms contains Entrez IDs and gene symbols.

    +

    The clusterProfiler function we will use requires a data +frame with two columns, where one column contains the term identifier or +name and one column contains gene identifiers that match our gene lists +we want to check for enrichment. Our data frame with KEGG terms contains +Entrez IDs and gene symbols.

    Read in DGE results and prep

    @@ -627,18 +3222,15 @@

    Read in DGE results and prep

    vs_low_df <- readr::read_tsv(vs_low_file)
    - -
    
    +
    +
    Rows: 35429 Columns: 7
     ── Column specification ────────────────────────────────────────────────────────
    -cols(
    -  Gene = col_character(),
    -  baseMean = col_double(),
    -  log2FoldChange = col_double(),
    -  lfcSE = col_double(),
    -  stat = col_double(),
    -  pvalue = col_double(),
    -  padj = col_double()
    -)
    +Delimiter: "\t" +chr (1): Gene +dbl (6): baseMean, log2FoldChange, lfcSE, stat, pvalue, padj + +ℹ Use `spec()` to retrieve the full column specification for this data. +ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
    @@ -657,92 +3249,85 @@

    Read in DGE results and prep

    Gene identifier conversion

    -

    Our data frame of DGE results contains Ensembl gene identifiers. So we will need to convert from these identifiers into either the gene symbols or Entrez IDs that are present in the data we extracted with msigdbr().

    -

    We’re going to convert our identifiers to gene symbols because they are a bit more human readable, but you can, with the change of a single argument, use the same code to convert to many other types of identifiers!

    -

    The annotation package org.Mm.eg.db contains information for different identifiers. org.Mm.eg.db is specific to Mus musculus – this is what the Mm in the package name is referencing. To perform gene identifier conversion in human (Homo sapiens) we could use org.Hs.eg.db; we would use org.Dr.eg.db for zebrafish (Danio rerio).

    -

    We can see what types of IDs are available to us in an annotation package with keytypes().

    +

    Our data frame of DGE results contains Ensembl gene identifiers. So +we will need to convert from these identifiers into either the gene +symbols or Entrez IDs that are present in the data we extracted with +msigdbr().

    +

    We’re going to convert our identifiers to gene symbols because they +are a bit more human readable, but you can, with the change of a single +argument, use the same code to convert to many other types of +identifiers!

    +

    The annotation package org.Mm.eg.db contains information +for different identifiers. org.Mm.eg.db is specific to +Mus musculus – this is what the Mm in the package +name is referencing. To perform gene identifier conversion in human +(Homo sapiens) we could use org.Hs.eg.db; we would +use org.Dr.eg.db for zebrafish (Danio rerio).

    +

    We can see what types of IDs are available to us in an annotation +package with keytypes().

    keytypes(org.Mm.eg.db)
    - +
     [1] "ACCNUM"       "ALIAS"        "ENSEMBL"      "ENSEMBLPROT"  "ENSEMBLTRANS"
      [6] "ENTREZID"     "ENZYME"       "EVIDENCE"     "EVIDENCEALL"  "GENENAME"    
    -[11] "GO"           "GOALL"        "IPI"          "MGI"          "ONTOLOGY"    
    -[16] "ONTOLOGYALL"  "PATH"         "PFAM"         "PMID"         "PROSITE"     
    -[21] "REFSEQ"       "SYMBOL"       "UNIGENE"      "UNIPROT"     
    +[11] "GENETYPE" "GO" "GOALL" "IPI" "MGI" +[16] "ONTOLOGY" "ONTOLOGYALL" "PATH" "PFAM" "PMID" +[21] "PROSITE" "REFSEQ" "SYMBOL" "UNIPROT"
    - +
    ACCNUM
    -
     ALIAS
    -
     ENSEMBL
    -
     ENSEMBLPROT
    -
     ENSEMBLTRANS
    -
     ENTREZID
    -
     ENZYME
    -
     EVIDENCE
    -
     EVIDENCEALL
    -
     GENENAME
    -
    +GENETYPE
     GO
    -
     GOALL
    -
     IPI
    -
     MGI
    -
     ONTOLOGY
    -
     ONTOLOGYALL
    -
     PATH
    -
     PFAM
    -
     PMID
    -
     PROSITE
    -
     REFSEQ
    -
     SYMBOL
    -
    -UNIGENE
    -
     UNIPROT
    -

    Even though we’ll use this package to convert from Ensembl gene IDs (ENSEMBL) to gene symbols (SYMBOL), we could just as easily use it to convert from an Ensembl transcript ID (ENSEMBLTRANS) to Entrez IDs (ENTREZID).

    -

    The function we will use to map from Ensembl gene IDs to gene symbols is called mapIds().

    +

    Even though we’ll use this package to convert from Ensembl gene IDs +(ENSEMBL) to gene symbols (SYMBOL), we could +just as easily use it to convert from an Ensembl transcript ID +(ENSEMBLTRANS) to Entrez IDs (ENTREZID).

    +

    The function we will use to map from Ensembl gene IDs to gene symbols +is called mapIds().

    - +
    # This returns a named vector which we can convert to a data frame, where
     # the keys (Ensembl IDs) are the names
     symbols_vector <- mapIds(org.Mm.eg.db,  # Specify the annotation package
    -                         # The vector of gene identifiers we want to 
    +                         # The vector of gene identifiers we want to
                              # map
    -                         keys = vs_low_df$Gene, 
    +                         keys = vs_low_df$Gene,
                              # What type of gene identifiers we're starting
                              # with
    -                         keytype = "ENSEMBL", 
    +                         keytype = "ENSEMBL",
                              # The type of gene identifier we want returned
    -                         column = "SYMBOL", 
    +                         column = "SYMBOL",
                              # In the case of 1:many mappings, return the
                              # first one. This is default behavior!
    -                         multiVals = "first") 
    + multiVals = "first")
    'select()' returned 1:many mapping between keys and columns
    @@ -756,13 +3341,19 @@

    Gene identifier conversion

    -

    This message is letting us know that sometimes Ensembl gene identifiers will map to multiple gene symbols. In this case, it’s also possible that a gene symbol will map to multiple Ensembl IDs.

    -

    Now we are ready to add the gene symbols to our data frame with the DGE results. We can use a join function from the dplyr package to do this, which will use the Ensembl gene IDs in both data frames to determine how to join the rows.

    -

    Let’s do this first for the comparison to the low stem cell capacity population.

    +

    This message is letting us know that sometimes Ensembl gene +identifiers will map to multiple gene symbols. In this case, it’s also +possible that a gene symbol will map to multiple Ensembl IDs.

    +

    Now we are ready to add the gene symbols to our data frame with the +DGE results. We can use a join function from the +dplyr package to do this, which will use the Ensembl gene +IDs in both data frames to determine how to join the rows.

    +

    Let’s do this first for the comparison to the low stem cell capacity +population.

    - -
    vs_low_df <- symbols_df %>%
    +
    +
    vs_low_df <- symbols_df |>
       # An *inner* join will only return rows that are in both data frames
       dplyr::inner_join(vs_low_df,
                         # The name of the column that contains the Ensembl gene IDs
    @@ -774,37 +3365,43 @@ 

    Gene identifier conversion

    Drop NA values

    -

    Some of these rows have NA values in padj, which can happen for a number of reasons including when all samples have zero counts or a gene has low mean expression.

    -

    Let’s filter to rows that do not have any NA using a function tidyr::drop_na(). This will also drop genes that have an Ensembl gene identifier but no gene symbol!

    +

    Some of these rows have NA values in padj, +which can +happen for a number of reasons including when all samples have zero +counts or a gene has low mean expression.

    +

    Let’s filter to rows that do not have any NA +using a function tidyr::drop_na(). This will also drop +genes that have an Ensembl gene identifier but no gene symbol!

    - -
    # Remove rows that are not complete (e.g., contain NAs) by filtering to only 
    +
    +
    # Remove rows that are not complete (e.g., contain NAs) by filtering to only
     # complete rows
    -vs_low_df <- vs_low_df %>%
    +vs_low_df <- vs_low_df |>
       tidyr::drop_na()
    -

    Now we’ll read in our data frame of DGE results from another comparison. To save us some time during instruction, we’ve already done the gene identifier conversion and filtering to remove NA values in this notebook. We took a different series of steps to achieve the same thing, which is often possible in R!

    +

    Now we’ll read in our data frame of DGE results from another +comparison. To save us some time during instruction, we’ve +already done the gene identifier conversion and filtering to remove +NA values in this +notebook. We took a different series of steps to achieve the same +thing, which is often possible in R!

    vs_unsorted_df <- readr::read_tsv(vs_unsorted_file)
    - -
    
    +
    +
    Rows: 17151 Columns: 8
     ── Column specification ────────────────────────────────────────────────────────
    -cols(
    -  Gene = col_character(),
    -  baseMean = col_double(),
    -  log2FoldChange = col_double(),
    -  lfcSE = col_double(),
    -  stat = col_double(),
    -  pvalue = col_double(),
    -  padj = col_double(),
    -  gene_symbol = col_character()
    -)
    +Delimiter: "\t" +chr (2): Gene, gene_symbol +dbl (6): baseMean, log2FoldChange, lfcSE, stat, pvalue, padj + +ℹ Use `spec()` to retrieve the full column specification for this data. +ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
    @@ -812,14 +3409,27 @@

    Drop NA values

    Over-representation Analysis (ORA)

    -

    To test for over-representation, we can calculate a p-value with a hypergeometric test (ref).

    -

    \(p = 1 - \displaystyle\sum_{i = 0}^{k-1}\frac{ {M \choose i}{ {N-M} \choose {n-i} } } { {N \choose n} }\)

    -

    Where N is the number of genes in the background distribution, M is the number of genes in a pathway, n is the number of genes we are interested in (our marker genes), and k is the number of genes that overlap between the pathway and our marker genes.

    -

    Borrowing an example from clusterProfiler: universal enrichment tool for functional and comparative study (Yu ):

    +

    To test for over-representation, we can calculate a p-value with a +hypergeometric test (ref).

    +

    \(p = 1 - \displaystyle\sum_{i = +0}^{k-1}\frac{ {M \choose i}{ {N-M} \choose {n-i} } } { {N \choose n} +}\)

    +

    Where N is the number of genes in the background +distribution, M is the number of genes in a pathway, +n is the number of genes we are interested in (our marker +genes), and k is the number of genes that overlap between +the pathway and our marker genes.

    +

    Borrowing an example from clusterProfiler: +universal enrichment tool for functional and comparative study (Yu +):

    -

    Example: Suppose we have 17,980 genes detected in a Microarray study and 57 genes were differentially expressed. Among the differential expressed genes, 28 are annotated to a gene set.

    +

    Example: Suppose we have 17,980 genes detected in a +Microarray study and 57 genes were differentially expressed. Among the +differential expressed genes, 28 are annotated to a gene set.

    -

    We’ll call genes that are differentially expressed gene_in_interest and genes that are in the gene set in_gene_set.

    +

    We’ll call genes that are differentially expressed +gene_in_interest and genes that are in the gene set +in_gene_set.

    @@ -838,7 +3448,10 @@

    Over-representation Analysis (ORA)

    -

    We can assess if the 28 overlapping genes mean that the differentially expressed genes are over-represented in the gene set with the hypergeometric distribution. This corresponds to a one-sided Fisher’s exact test.

    +

    We can assess if the 28 overlapping genes mean that the +differentially expressed genes are over-represented in the gene set with +the hypergeometric distribution. This corresponds to a one-sided +Fisher’s exact test.

    @@ -859,56 +3472,88 @@

    Over-representation Analysis (ORA)

    -

    When we test multiple pathways or gene sets, the p-values then need to be adjusted for multiple hypothesis testing.

    +

    When we test multiple pathways or gene sets, the +p-values then need to be adjusted for multiple +hypothesis testing.

    High stem cell capacity ORA

    -

    Our DGE results are from data published as part of Sachs et al. (2014). The authors sorted populations of primary leukemia cells and examined the stem cell capacity of these cell populations. (This study may sound familiar if you’ve worked on one of our bulk RNA-seq exercise notebooks in the past!)

    -

    We compared the population that the authors identified as having high stem cell capacity to a low stem cell capacity population. We also compared the high stem cell capacity cells to a mix of populations (e.g., unsorted cells). You can see the code in here.

    -

    We’re interested in what pathways are over-represented in genes that specifically distinguish the high capacity population from the low capacity population.

    -

    Let’s generate a list of genes that have higher expression in the high stem cell capacity population compared to the low stem cell capacity population, but we’ll also want to exclude genes that show up in our other comparison to unsorted cells.

    -

    We’ll start with the high stem cell capacity vs. low stem cell capacity population comparison. Genes with positive log2 fold-changes (LFC) will be more highly expressed in the high stem cell capacity cells based on how we set up the analysis.

    +

    Our DGE results are from data published as part of Sachs et +al. (2014). The authors sorted populations of primary leukemia +cells and examined the stem cell capacity of these cell populations. +(This study may sound familiar if you’ve worked on one of our bulk +RNA-seq exercise notebooks in the past!)

    +

    We compared the population that the authors identified as having high +stem cell capacity to a low stem cell capacity population. We also +compared the high stem cell capacity cells to a mix of populations +(e.g., unsorted cells). You can see the code in here.

    +

    We’re interested in what pathways are over-represented in genes that +specifically distinguish the high capacity population from the low +capacity population.

    +

    Let’s generate a list of genes that have higher expression in the +high stem cell capacity population compared to the low stem cell +capacity population, but we’ll also want to exclude genes that show up +in our other comparison to unsorted cells.

    +

    We’ll start with the high stem cell capacity vs. low stem cell +capacity population comparison. Genes with positive log2 fold-changes +(LFC) will be more highly expressed in the high stem cell capacity cells +based on how we set up the analysis.

    - -
    vs_low_genes <- vs_low_df %>%
    +
    +
    vs_low_genes <- vs_low_df |>
       # Filter to the positive LFC and filter based on significance too (padj)
       dplyr::filter(log2FoldChange > 0,
    -                padj < 0.05) %>%
    +                padj < 0.05) |>
       # Return a vector of gene symbols
       dplyr::pull(gene_symbol)
    -

    Although we’re picking a commonly used cutoff (FDR < 0.05), it’s still arbitrary and we could just as easily pick a different threshold for our LFC values. When we generate lists of genes of interest for ORA, we typically pick an arbitrary cutoff. This is one of the approach’s weaknesses – we’ve removed all other context.

    +

    Although we’re picking a commonly used cutoff (FDR < +0.05), it’s still arbitrary and we could just as easily pick a different +threshold for our LFC values. When we generate lists of genes of +interest for ORA, we typically pick an arbitrary cutoff. This is one of +the approach’s weaknesses – we’ve removed all other context.

    Now, we’ll take the same steps for our other results.

    - -
    vs_unsorted_genes <- vs_unsorted_df %>%
    +
    +
    vs_unsorted_genes <- vs_unsorted_df |>
       dplyr::filter(log2FoldChange > 0,
    -                padj < 0.05) %>%
    +                padj < 0.05) |>
       dplyr::pull(gene_symbol)
    -

    We want genes that are in the first comparison but not in the second! We can use setdiff(), a base R function for set operations, to get the list that we want.

    +

    We want genes that are in the first comparison but not in the second! +We can use setdiff(), a base R function for set operations, +to get the list that we want.

    - +
    # What genes are in the first set but *not* in the second set
    -genes_for_ora <- setdiff(vs_low_genes, vs_unsorted_genes) 
    +genes_for_ora <- setdiff(vs_low_genes, vs_unsorted_genes)

    Background set

    -

    As we saw above, calculating the p-value relies on the number of genes in the background distribution. Sometimes folks consider genes from the entire genome to comprise the background, but in the example borrowed from the clusterProfiler authors, they state:

    +

    As we saw above, calculating the p-value relies on the number of +genes in the background distribution. Sometimes folks consider genes +from the entire genome to comprise the background, but in the example +borrowed from the clusterProfiler authors, they state:

    17,980 genes detected in a Microarray study

    Where the key phrase is genes detected.

    -

    If we were unable to include a gene in one of our differential expression comparisons because, for example, it had low mean expression in our experiment and therefore was filtered out in our tidyr::drop_na() step, we shouldn’t include in our background set.

    -

    We can use another function for set operations, intersect(), to get our background set of genes that were included in both comparisons.

    +

    If we were unable to include a gene in one of our differential +expression comparisons because, for example, it had low mean expression +in our experiment and therefore was filtered out in our +tidyr::drop_na() step, we shouldn’t include in our +background set.

    +

    We can use another function for set operations, +intersect(), to get our background set of genes that were +included in both comparisons.

    @@ -927,25 +3572,29 @@

    Background set

    Run enricher()

    -

    Now that we have our background set, our genes of interest, and our pathway information, we’re ready to run ORA using the enricher() function.

    +

    Now that we have our background set, our genes of interest, and our +pathway information, we’re ready to run ORA using the +enricher() function.

    - +
    kegg_ora_results <- enricher(
       gene = genes_for_ora,  # Genes of interest
    -  pvalueCutoff = 0.05,  
    +  pvalueCutoff = 0.05,
       pAdjustMethod = "BH",  # FDR
       universe = background_set,  # Background set
    -  # The pathway information should be a data frame with a term name or 
    +  # The pathway information should be a data frame with a term name or
       # identifier and the gene identifiers
    -  TERM2GENE = dplyr::select(mm_kegg_df,  
    +  TERM2GENE = dplyr::select(mm_kegg_df,
                                 gs_name,
                                 gene_symbol)
     )
    -

    Note: using enrichKEGG() is a shortcut for doing ORA using KEGG, but the approach we covered here can be used with any gene sets you’d like!

    +

    Note: using enrichKEGG() is a shortcut for doing ORA +using KEGG, but the approach we covered here can be used with any gene +sets you’d like!

    What is returned by enricher()?

    @@ -954,7 +3603,9 @@

    Run enricher()

    -

    The information we’re most likely interested in is in the results slot. Let’s convert this into a data frame that we can write to file.

    +

    The information we’re most likely interested in is in the +results slot. Let’s convert this into a data frame that we +can write to file.

    @@ -965,43 +3616,47 @@

    Run enricher()

    Visualizing results

    -

    We can use a dot plot to visualize our significant enrichment results.

    +

    We can use a dot plot to visualize our significant enrichment +results.

    enrichplot::dotplot(kegg_ora_results)
    - -
    wrong orderBy parameter; set to default `orderBy = "x"`
    - -

    +

    -

    We can use an UpSet plot to visualize the overlap between the gene sets that were returned as significant.

    +

    We can use an UpSet +plot to visualize the overlap between the gene sets +that were returned as significant.

    enrichplot::upsetplot(kegg_ora_results)
    -

    +

    -

    We can see that some of the DNA repair pathways share genes. Gene sets or pathways aren’t independent, either! Sometimes multiple pathways that show up in our results as significant are indicative of only a handful of genes in our gene list.

    -

    We can look at the geneID column of our results to see what genes overlap; it’s a good idea to take a look.

    +

    We can see that some of the DNA repair pathways share genes. Gene +sets or pathways aren’t independent, either! Sometimes multiple pathways +that show up in our results as significant are indicative of only a +handful of genes in our gene list.

    +

    We can look at the geneID column of our results to see +what genes overlap; it’s a good idea to take a look.

    - -
    kegg_result_df %>%
    +
    +
    kegg_result_df |>
       # Use dplyr::select() - the name of the pathway is in the ID column
       dplyr::select(ID, geneID)
    @@ -1026,67 +3681,76 @@

    Session Info

    sessionInfo()
    - -
    R version 4.0.3 (2020-10-10)
    -Platform: x86_64-pc-linux-gnu (64-bit)
    -Running under: Ubuntu 20.04 LTS
    +
    +
    R version 4.4.1 (2024-06-14)
    +Platform: x86_64-pc-linux-gnu
    +Running under: Ubuntu 22.04.4 LTS
     
     Matrix products: default
    -BLAS/LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.8.so
    +BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
    +LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0
     
     locale:
      [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
      [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
    - [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
    + [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
      [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
      [9] LC_ADDRESS=C               LC_TELEPHONE=C            
     [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
     
    +time zone: Etc/UTC
    +tzcode source: system (glibc)
    +
     attached base packages:
    -[1] parallel  stats4    stats     graphics  grDevices utils     datasets 
    -[8] methods   base     
    +[1] stats4    stats     graphics  grDevices utils     datasets  methods  
    +[8] base     
     
     other attached packages:
    - [1] org.Mm.eg.db_3.12.0    AnnotationDbi_1.52.0   IRanges_2.24.1        
    - [4] S4Vectors_0.28.1       Biobase_2.50.0         BiocGenerics_0.36.0   
    - [7] msigdbr_7.2.1          clusterProfiler_3.18.1 magrittr_2.0.1        
    -[10] optparse_1.6.6        
    +[1] org.Mm.eg.db_3.19.1    AnnotationDbi_1.66.0   IRanges_2.38.0        
    +[4] S4Vectors_0.42.0       Biobase_2.64.0         BiocGenerics_0.50.0   
    +[7] msigdbr_7.5.1          clusterProfiler_4.12.0 optparse_1.7.5        
     
     loaded via a namespace (and not attached):
    - [1] enrichplot_1.10.2   bit64_4.0.5         RColorBrewer_1.1-2 
    - [4] tools_4.0.3         R6_2.5.0            DBI_1.1.1          
    - [7] colorspace_2.0-0    tidyselect_1.1.0    gridExtra_2.3      
    -[10] bit_4.0.4           compiler_4.0.3      cli_2.2.0          
    -[13] scatterpie_0.1.5    labeling_0.4.2      shadowtext_0.0.7   
    -[16] scales_1.1.1        readr_1.4.0         stringr_1.4.0      
    -[19] digest_0.6.27       ggupset_0.3.0       rmarkdown_2.6      
    -[22] DOSE_3.16.0         pkgconfig_2.0.3     htmltools_0.5.1.1  
    -[25] fastmap_1.1.0       rlang_0.4.10        rstudioapi_0.13    
    -[28] RSQLite_2.2.3       farver_2.0.3        generics_0.1.0     
    -[31] jsonlite_1.7.2      BiocParallel_1.24.1 GOSemSim_2.16.1    
    -[34] dplyr_1.0.3         GO.db_3.12.1        Matrix_1.3-2       
    -[37] fansi_0.4.2         Rcpp_1.0.6          munsell_0.5.0      
    -[40] viridis_0.5.1       lifecycle_0.2.0     stringi_1.5.3      
    -[43] yaml_2.2.1          ggraph_2.0.4        MASS_7.3-53        
    -[46] plyr_1.8.6          qvalue_2.22.0       grid_4.0.3         
    -[49] blob_1.2.1          ggrepel_0.9.1       DO.db_2.9          
    -[52] crayon_1.3.4        lattice_0.20-41     graphlayouts_0.7.1 
    -[55] cowplot_1.1.1       splines_4.0.3       hms_1.0.0          
    -[58] ps_1.5.0            knitr_1.30          pillar_1.4.7       
    -[61] fgsea_1.16.0        igraph_1.2.6        reshape2_1.4.4     
    -[64] fastmatch_1.1-0     glue_1.4.2          evaluate_0.14      
    -[67] downloader_0.4      data.table_1.13.6   BiocManager_1.30.10
    -[70] vctrs_0.3.6         tweenr_1.0.1        gtable_0.3.0       
    -[73] getopt_1.20.3       purrr_0.3.4         polyclip_1.10-0    
    -[76] tidyr_1.1.2         assertthat_0.2.1    cachem_1.0.1       
    -[79] ggplot2_3.3.3       xfun_0.20           ggforce_0.3.2      
    -[82] tidygraph_1.2.0     viridisLite_0.3.0   tibble_3.0.5       
    -[85] rvcheck_0.1.8       memoise_1.1.0       ellipsis_0.3.1     
    + [1] DBI_1.2.2 gson_0.1.0 shadowtext_0.1.3 + [4] gridExtra_2.3 rlang_1.1.3 magrittr_2.0.3 + [7] DOSE_3.30.0 compiler_4.4.1 RSQLite_2.3.6 + [10] png_0.1-8 vctrs_0.6.5 reshape2_1.4.4 + [13] stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2 + [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3 + [19] ggraph_2.2.1 utf8_1.2.4 HDO.db_0.99.1 + [22] rmarkdown_2.26 tzdb_0.4.0 enrichplot_1.24.0 + [25] UCSC.utils_1.0.0 purrr_1.0.2 bit_4.0.5 + [28] xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8 + [31] aplot_0.2.2 GenomeInfoDb_1.40.0 jsonlite_1.8.8 + [34] blob_1.2.4 highr_0.10 BiocParallel_1.38.0 + [37] tweenr_2.0.3 parallel_4.4.1 R6_2.5.1 + [40] bslib_0.7.0 stringi_1.8.3 RColorBrewer_1.1-3 + [43] jquerylib_0.1.4 GOSemSim_2.30.0 Rcpp_1.0.12 + [46] knitr_1.46 readr_2.1.5 Matrix_1.7-0 + [49] splines_4.4.1 igraph_2.0.3 tidyselect_1.2.1 + [52] qvalue_2.36.0 yaml_2.3.8 viridis_0.6.5 + [55] codetools_0.2-20 lattice_0.22-6 tibble_3.2.1 + [58] plyr_1.8.9 treeio_1.28.0 withr_3.0.0 + [61] KEGGREST_1.44.0 evaluate_0.23 gridGraphics_0.5-1 + [64] scatterpie_0.2.2 getopt_1.20.4 polyclip_1.10-6 + [67] ggupset_0.3.0 Biostrings_2.72.0 ggtree_3.12.0 + [70] pillar_1.9.0 ggfun_0.1.4 generics_0.1.3 + [73] vroom_1.6.5 hms_1.1.3 ggplot2_3.5.1 + [76] tidytree_0.4.6 munsell_0.5.1 scales_1.3.0 + [79] glue_1.7.0 lazyeval_0.2.2 tools_4.4.1 + [82] data.table_1.15.4 fgsea_1.30.0 babelgene_22.9 + [85] fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4 + [88] tidygraph_1.3.1 cowplot_1.1.3 grid_4.4.1 + [91] ape_5.8 tidyr_1.3.1 colorspace_2.1-0 + [94] nlme_3.1-164 patchwork_1.2.0 GenomeInfoDbData_1.2.12 + [97] ggforce_0.4.2 cli_3.6.2 fansi_1.0.6 +[100] viridisLite_0.4.2 + [ reached getOption("max.print") -- omitted 15 entries ]
    -
    LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogT3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjAKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBQZXJmb3JtIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIHdpdGggW2BBbm5vdGF0aW9uREJJYCBhbm5vdGF0aW9uIHBhY2thZ2VzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9Bbm5vdGF0aW9uRGJpL2luc3QvZG9jL0ludHJvVG9Bbm5vdGF0aW9uUGFja2FnZXMucGRmKQotIEFjY2VzcyBbTW9sZWN1bGFyIFNpZ25hdHVyZXMgRGF0YWJhc2UgZ2VuZSBzZXQgY29sbGVjdGlvbnNdKGh0dHBzOi8vd3d3LmdzZWEtbXNpZ2RiLm9yZy9nc2VhL21zaWdkYi9jb2xsZWN0aW9ucy5qc3ApIHZpYSB0aGUgYG1zaWdkYnJgIHBhY2thZ2UKLSBQcmVwYXJlIGdlbmUgc2V0cyBmb3Igb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgaW5jbHVkaW5nIGFuIGFwcHJvcHJpYXRlIGJhY2tncm91bmQgc2V0Ci0gUGVyZm9ybSBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIHdpdGggdGhlIGBjbHVzdGVyUHJvZmlsZXJgIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgY292ZXIgYSB0eXBlIG9mIHBhdGh3YXkgb3IgZ2VuZSBzZXQgYW5hbHlzaXMgY2FsbGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgKE9SQSkuClRoZSBpZGVhIGJlaGluZCBPUkEgaXMgcmVsYXRpdmVseSBzdHJhaWdodGZvcndhcmQ6IGdpdmVuIGEgc2V0IG9mIGdlbmVzLCBkbyB0aGVzZSBnZW5lcyBvdmVybGFwIHdpdGggYSBwYXRod2F5IG1vcmUgdGhhbiB3ZSBleHBlY3QgYnkgY2hhbmNlPwpUaGUgc2ltcGxpY2l0eSBvZiBvbmx5IHJlcXVpcmluZyBhbiBpbnB1dCBnZW5lIHNldCAoc29ydCBvZiwgbW9yZSBvbiB0aGF0IGJlbG93KSBjYW4gYmUgYXR0cmFjdGl2ZS4KCk9SQSBoYXMgc29tZSBsaW1pdGF0aW9ucywgb3V0bGluZWQgbmljZWx5IChhbmQgbW9yZSBleHRlbnNpdmVseSEpIGluIFtLaGF0cmkgX2V0IGFsLl8gKDIwMTIpXSggaHR0cHM6Ly9kb2kub3JnLzEwLjEzNzEvam91cm5hbC5wY2JpLjEwMDIzNzUpLiAKT25lIG9mIHRoZSBtYWluIGlzc3VlcyB3aXRoIE9SQSBpcyB0aGF0IHR5cGljYWxseSBhbGwgZ2VuZXMgYXJlIHRyZWF0ZWQgYXMgZXF1YWwgLS0gdGhlIGNvbnRleHQgb2YgdGhlIG1hZ25pdHVkZSBvZiBhIGNoYW5nZSB3ZSBtYXkgYmUgbWVhc3VyaW5nIGlzIHJlbW92ZWQgYW5kIGVhY2ggZ2VuZSBpcyB0cmVhdGVkIGFzIGluZGVwZW5kZW50LCB3aGljaCBjYW4gc29tZXRpbWVzIHJlc3VsdCBpbiBhbiBpbmNvcnJlY3QgZXN0aW1hdGUgb2Ygc2lnbmlmaWNhbmNlLgoKV2Ugd2lsbCB1c2UgdGhlIFtgY2x1c3RlclByb2ZpbGVyYCBwYWNrYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvY2x1c3RlclByb2ZpbGVyLmh0bWwpIChbWXUgKmV0IGFsLiogMjAxMl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwODkvb21pLjIwMTEuMDExOC4pKSB0byBwZXJmb3JtIE9SQS4gCmBjbHVzdGVyUHJvZmlsZXJgIGhhcyBtYW55IGJ1aWx0LWluIGZ1bmN0aW9ucyB0aGF0IHdpbGwgcnVuIGEgc3BlY2lmaWMgdHlwZSBvZiBhbmFseXNpcyB1c2luZyBhIHNwZWNpZmljIHNvdXJjZSBvZiBwYXRod2F5cy9nZW5lIHNldHMgYXV0b21hdGljYWxseSwgYnV0IGZvciBvdXIgcHVycG9zZXMgd2UncmUgZ29pbmcgdG8ga2VlcCB0aGluZ3MgYXMgZ2VuZXJhbCBhcyBwb3NzaWJsZS4KU2VlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgYm9va10oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2luZGV4Lmh0bWwpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwYWNrYWdlJ3MgZnVsbCBzdWl0ZSBvZiBmdW5jdGlvbmFsaXR5LgoKQmVjYXVzZSBkaWZmZXJlbnQgYmlvaW5mb3JtYXRpY3MgdG9vbHMgb2Z0ZW4gcmVxdWlyZSBkaWZmZXJlbnQgdHlwZXMgb2YgZ2VuZSBpZGVudGlmaWVycywgd2UnbGwgYWxzbyBjb3ZlciBob3cgdG8gY29udmVydCBiZXR3ZWVuIGdlbmUgaWRlbnRpZmllcnMgdXNpbmcgW2BBbm5vdGF0aW9uRGJpYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0Fubm90YXRpb25EYmkuaHRtbCkgQmlvY29uZHVjdG9yIHBhY2thZ2VzIGluIHRoaXMgbm90ZWJvb2suCkNoZWNrIG91dCB0aGUgW19Bbm5vdGF0aW9uRGJpOiBJbnRyb2R1Y3Rpb24gVG8gQmlvY29uZHVjdG9yIEFubm90YXRpb24gUGFja2FnZXNfIChDYXJsc29uIDIwMjAuKSB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvQW5ub3RhdGlvbkRiaS9pbnN0L2RvYy9JbnRyb1RvQW5ub3RhdGlvblBhY2thZ2VzLnBkZikgZm9yIG1vcmUgaW5mb3JtYXRpb24uCgojIyMjIE90aGVyIHJlc291cmNlcwoKKiBGb3IgYW5vdGhlciBleGFtcGxlIHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgLCBzZWUgW19JbnRybyB0byBER0U6IEZ1bmN0aW9uYWwgQW5hbHlzaXMuXyBmcm9tIEhhcnZhcmQgQ2hhbiBCaW9pbmZvcm1hdGljcyBDb3JlIFRyYWluaW5nLl0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDlfZnVuY3Rpb25hbF9hbmFseXNpcy5odG1sKQoqIFtgV2ViR2VzdGFsdFJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvV2ViR2VzdGFsdFIvKSBpcyBhbm90aGVyIFIgcGFja2FnZSB0aGF0IGNhbiBiZSB1c2VkIGZvciBPUkEuCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMKCmBgYHtyIGxpYnJhcmllc30KIyBQaXBlcwpsaWJyYXJ5KG1hZ3JpdHRyKQojIFBhY2thZ2Ugd2UnbGwgdXNlIHRvIApsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKIyBQYWNrYWdlIHRoYXQgY29udGFpbnMgTVNpZ0RCIGdlbmUgc2V0cyBpbiB0aWR5IGZvcm1hdApsaWJyYXJ5KG1zaWdkYnIpCiMgTXVzIG11c2N1bHVzIGFubm90YXRpb24gcGFja2FnZSB3ZSdsbCB1c2UgZm9yIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uCmxpYnJhcnkob3JnLk1tLmVnLmRiKQpgYGAKCiMjIyBEaXJlY3RvcmllcyBhbmQgZmlsZXMKCiMjIyMgRGlyZWN0b3JpZXMKCmBgYHtyIGNyZWF0ZV9vcmFfZGlyZWN0b3J5LCBsaXZlID0gVFJVRX0KIyBXZSdsbCBjcmVhdGUgYSBkaXJlY3RvcnkgdG8gc3BlY2lmaWNhbGx5IGhvbGQgdGhlIE9SQSByZXN1bHRzIGlmIGl0IGRvZXNuJ3QKIyBleGlzdCB5ZXQKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgImxldWtlbWlhIikKaWYgKCFkaXIuZXhpc3RzKHJlc3VsdHNfZGlyKSkgewogIGRpci5jcmVhdGUocmVzdWx0c19kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgojIyMjIElucHV0IGZpbGVzCgpGb3Igb3VyIE9SQSBleGFtcGxlLCB3ZSdyZSBnb2luZyB0byB1c2UgdHdvIHRhYmxlcyBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIChER0UpIGFuYWx5c2lzIHJlc3VsdHMuCgpgYGB7ciBpbnB1dF9kaXJlY3Rvcnl9CmlucHV0X2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAibGV1a2VtaWEiKQoKIyBUaGlzIGZpbGUgY29udGFpbnMgdGhlIERHRSByZXN1bHRzIGZvciBhIGNlbGwgcG9wdWxhdGlvbiB3aXRoIGhpZ2ggc3RlbSBjZWxsCiMgY2FwYWNpdHkgYXMgY29tcGFyZWQgdG8gYSBjZWxsIHBvcHVsYXRpb24gCnZzX2xvd19maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hfY2FwYWNpdHlfdnNfbG93X2NhcGFjaXR5X3Jlc3VsdHMudHN2IikKCiMgVGhpcyBmaWxlIGNvbnRhaW5zIHRoZSBER0UgcmVzdWx0cyBmb3IgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24KIyB2cy4gdW5zb3J0ZWQgY2VsbHMgKGUuZy4sIGEgbWl4dHVyZSBvZiBjYXBhY2l0aWVzKQp2c191bnNvcnRlZF9maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoX2NhcGFjaXR5X3ZzX3Vuc29ydGVkX3Jlc3VsdHMudHN2IikKYGBgCgojIyMjIE91dHB1dCBmaWxlcwoKV2UnbGwgc2F2ZSB0aGUgdGFibGUgb2YgT1JBIHJlc3VsdHMgKGUuZy4sIHAtdmFsdWVzKS4KCmBgYHtyIG91dHB1dF9maWxlLCBsaXZlID0gVFJVRX0Ka2VnZ19yZXN1bHRzX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAibGV1a2VtaWFfa2VnZ19vcmFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgc2V0cwoKV2Ugd2lsbCB1c2UgZ2VuZSBzZXRzIGZyb20gdGhlIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKV0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2luZGV4LmpzcCkgZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlIChbU3VicmFtYW5pYW4sIFRhbWF5byAqZXQgYWwuKiAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpKS4gClRoZSBbYG1zaWdkYnJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbXNpZ2Rici9pbmRleC5odG1sKSBwYWNrYWdlIGNvbnRhaW5zIE1TaWdEQiBkYXRhc2V0cyBhbHJlYWR5IGluIHRoZSB0aWR5IGZvcm1hdCByZXF1aXJlZCBieSBgY2x1c3RlclByb2ZpbGVyYCBhbmQgc3VwcG9ydHMgbXVsdGlwbGUgb3JnYW5pc21zLgoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgd2hhdCBvcmdhbmlzbXMgdGhlIHBhY2thZ2Ugc3VwcG9ydHMuCgpgYGB7ciBzaG93X3NwZWNpZXN9Cm1zaWdkYnJfc3BlY2llcygpCmBgYAoKVGhlIHJlc3VsdHMgd2UncmUgaW50ZXJlc3RlZCBpbiBoZXJlIGNvbWUgZnJvbSBtb3VzZSBzYW1wbGVzLCBzbyB3ZSBjYW4gb2J0YWluIGp1c3QgdGhlIGdlbmUgc2V0cyByZWxldmFudCB0byBfTS4gbXVzY3VsdXNfIHdpdGggdGhlIGBzcGVjaWVzYCBhcmd1bWVudCB0byBgbXNpZ2RicigpYC4KCmBgYHtyIG1tX2RmLCBsaXZlID0gVFJVRX0KbW1fbXNpZ2RiX2RmIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJNdXMgbXVzY3VsdXMiKQpgYGAKCk1TaWdEQiBjb250YWlucyA4IGRpZmZlcmVudCBnZW5lIHNldCBjb2xsZWN0aW9ucy4KCiAgICBIOiBoYWxsbWFyayBnZW5lIHNldHMKICAgIEMxOiBwb3NpdGlvbmFsIGdlbmUgc2V0cwogICAgQzI6IGN1cmF0ZWQgZ2VuZSBzZXRzCiAgICBDMzogbW90aWYgZ2VuZSBzZXRzCiAgICBDNDogY29tcHV0YXRpb25hbCBnZW5lIHNldHMKICAgIEM1OiBHTyBnZW5lIHNldHMKICAgIEM2OiBvbmNvZ2VuaWMgc2lnbmF0dXJlcwogICAgQzc6IGltbXVub2xvZ2ljIHNpZ25hdHVyZXMKCkluIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgY2Fub25pY2FsIHBhdGh3YXlzIHdoaWNoIGFyZSAoW3JlZl0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25zLmpzcCkpOgoKPiBHZW5lIHNldHMgZnJvbSBwYXRod2F5IGRhdGFiYXNlcy4gVXN1YWxseSwgdGhlc2UgZ2VuZSBzZXRzIGFyZSBjYW5vbmljYWwgcmVwcmVzZW50YXRpb25zIG9mIGEgYmlvbG9naWNhbCBwcm9jZXNzIGNvbXBpbGVkIGJ5IGRvbWFpbiBleHBlcnRzLgoKQW5kIGFyZSBhIHN1YnNldCBvZiBgQzI6IGN1cmF0ZWQgZ2VuZSBzZXRzYC4KU3BlY2lmaWNhbGx5LCB3ZSB3aWxsIHVzZSB0aGUgW0tFR0cgKEt5b3RvIEVuY3ljbG9wZWRpYSBvZiBHZW5lcyBhbmQgR2Vub21lcyldKGh0dHBzOi8vd3d3Lmdlbm9tZS5qcC9rZWdnLykgcGF0aHdheXMuIAoKYGBge3IgZmlsdGVyX3RvX2tlZ2d9CiMgRmlsdGVyIHRoZSBtb3VzZSBkYXRhIGZyYW1lIHRvIHRoZSBLRUdHIHBhdGh3YXlzIHRoYXQgYXJlIGluY2x1ZGVkIGluIHRoZQojIGN1cmF0ZWQgZ2VuZSBzZXRzCm1tX2tlZ2dfZGYgPC0gbW1fbXNpZ2RiX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfY2F0ID09ICJDMiIsICAjIGN1cmF0ZWQgZ2VuZSBzZXRzIAogICAgICAgICAgICAgICAgZ3Nfc3ViY2F0ID09ICJDUDpLRUdHIikgICMgS0VHRyBwYXRod2F5cyAKYGBgCgoqTm90ZTogV2UgY291bGQgc3BlY2lmaWVkIHRoYXQgd2Ugd2FudGVkIHRoZSBLRUdHIGdlbmUgc2V0cyB1c2luZyB0aGUgYGNhdGVnb3J5YCBhbmQgYHN1YmNhdGVnb3J5YCBhcmd1bWVudHMgb2YgYG1zaWdkYnIoKWAsIGJ1dCB3ZSdyZSBnb2luZyBmb3IgZ2VuZXJhbCBzdGVwcyEqCgpgYGB7ciBtbV9rZWdnX2NvbHVtbnN9CmNvbG5hbWVzKG1tX2tlZ2dfZGYpCmBgYAoKVGhlIGBjbHVzdGVyUHJvZmlsZXJgIGZ1bmN0aW9uIHdlIHdpbGwgdXNlIHJlcXVpcmVzIGEgZGF0YSBmcmFtZSB3aXRoIHR3byBjb2x1bW5zLCB3aGVyZSBvbmUgY29sdW1uIGNvbnRhaW5zIHRoZSB0ZXJtIGlkZW50aWZpZXIgb3IgbmFtZSBhbmQgb25lIGNvbHVtbiBjb250YWlucyBnZW5lIGlkZW50aWZpZXJzIHRoYXQgbWF0Y2ggb3VyIGdlbmUgbGlzdHMgd2Ugd2FudCB0byBjaGVjayBmb3IgZW5yaWNobWVudC4KT3VyIGRhdGEgZnJhbWUgd2l0aCBLRUdHIHRlcm1zIGNvbnRhaW5zIEVudHJleiBJRHMgYW5kIGdlbmUgc3ltYm9scy4KCiMjIFJlYWQgaW4gREdFIHJlc3VsdHMgYW5kIHByZXAKCmBgYHtyIHJlYWRfaW5fZGdlX3Jlc3VsdHMsIGxpdmUgPSBUUlVFfQp2c19sb3dfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZzX2xvd19maWxlKQpgYGAKCkxldCdzIHRha2UgYSBwZWVrIGF0IHRoZSB0b3Agb2YgdGhlIERHRSByZXN1bHRzIGRhdGEgZnJhbWUuCgpgYGB7ciBwZWVrX2F0X2RnZV9yZXN1bHRzLCBsaXZlID0gVFJVRX0KaGVhZCh2c19sb3dfZGYpCmBgYAoKIyMjIEdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uCgpPdXIgZGF0YSBmcmFtZSBvZiBER0UgcmVzdWx0cyBjb250YWlucyBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMuClNvIHdlIHdpbGwgbmVlZCB0byBjb252ZXJ0IGZyb20gdGhlc2UgaWRlbnRpZmllcnMgaW50byBlaXRoZXIgdGhlIGdlbmUgc3ltYm9scyBvciBFbnRyZXogSURzIHRoYXQgYXJlIHByZXNlbnQgaW4gdGhlIGRhdGEgd2UgZXh0cmFjdGVkIHdpdGggYG1zaWdkYnIoKWAuCgpXZSdyZSBnb2luZyB0byBjb252ZXJ0IG91ciBpZGVudGlmaWVycyB0byBnZW5lIHN5bWJvbHMgYmVjYXVzZSB0aGV5IGFyZSBhIGJpdCBtb3JlIGh1bWFuIHJlYWRhYmxlLCBidXQgeW91IGNhbiwgd2l0aCB0aGUgY2hhbmdlIG9mIGEgc2luZ2xlIGFyZ3VtZW50LCB1c2UgdGhlIHNhbWUgY29kZSB0byBjb252ZXJ0IHRvIG1hbnkgb3RoZXIgdHlwZXMgb2YgaWRlbnRpZmllcnMhCgpUaGUgYW5ub3RhdGlvbiBwYWNrYWdlIGBvcmcuTW0uZWcuZGJgIGNvbnRhaW5zIGluZm9ybWF0aW9uIGZvciBkaWZmZXJlbnQgaWRlbnRpZmllcnMuCmBvcmcuTW0uZWcuZGJgIGlzIHNwZWNpZmljIHRvIF9NdXMgbXVzY3VsdXNfIC0tIHRoaXMgaXMgd2hhdCB0aGUgYE1tYCBpbiB0aGUgcGFja2FnZSBuYW1lIGlzIHJlZmVyZW5jaW5nLgpUbyBwZXJmb3JtIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIGluIGh1bWFuIChfSG9tbyBzYXBpZW5zXykgd2UgY291bGQgdXNlIGBvcmcuSHMuZWcuZGJgOwp3ZSB3b3VsZCB1c2UgYG9yZy5Eci5lZy5kYmAgZm9yIHplYnJhZmlzaCAoX0RhbmlvIHJlcmlvXykuCgpXZSBjYW4gc2VlIHdoYXQgdHlwZXMgb2YgSURzIGFyZSBhdmFpbGFibGUgdG8gdXMgaW4gYW4gYW5ub3RhdGlvbiBwYWNrYWdlIHdpdGggYGtleXR5cGVzKClgLgoKYGBge3Iga2V5dHlwZXMsIGxpdmUgPSBUUlVFfQprZXl0eXBlcyhvcmcuTW0uZWcuZGIpCmBgYAoKRXZlbiB0aG91Z2ggd2UnbGwgdXNlIHRoaXMgcGFja2FnZSB0byBjb252ZXJ0IGZyb20gRW5zZW1ibCBnZW5lIElEcyAoYEVOU0VNQkxgKSB0byBnZW5lIHN5bWJvbHMgKGBTWU1CT0xgKSwgd2UgY291bGQganVzdCBhcyBlYXNpbHkgdXNlIGl0IHRvIGNvbnZlcnQgZnJvbSBhbiBFbnNlbWJsIHRyYW5zY3JpcHQgSUQgKGBFTlNFTUJMVFJBTlNgKSB0byBFbnRyZXogSURzIChgRU5UUkVaSURgKS4KClRoZSBmdW5jdGlvbiB3ZSB3aWxsIHVzZSB0byBtYXAgZnJvbSBFbnNlbWJsIGdlbmUgSURzIHRvIGdlbmUgc3ltYm9scyBpcyBjYWxsZWQgYG1hcElkcygpYC4KCmBgYHtyIG1hcF90b19zeW1ib2x9CiMgVGhpcyByZXR1cm5zIGEgbmFtZWQgdmVjdG9yIHdoaWNoIHdlIGNhbiBjb252ZXJ0IHRvIGEgZGF0YSBmcmFtZSwgd2hlcmUKIyB0aGUga2V5cyAoRW5zZW1ibCBJRHMpIGFyZSB0aGUgbmFtZXMKc3ltYm9sc192ZWN0b3IgPC0gbWFwSWRzKG9yZy5NbS5lZy5kYiwgICMgU3BlY2lmeSB0aGUgYW5ub3RhdGlvbiBwYWNrYWdlCiAgICAgICAgICAgICAgICAgICAgICAgICAjIFRoZSB2ZWN0b3Igb2YgZ2VuZSBpZGVudGlmaWVycyB3ZSB3YW50IHRvIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBtYXAKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXMgPSB2c19sb3dfZGYkR2VuZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAjIFdoYXQgdHlwZSBvZiBnZW5lIGlkZW50aWZpZXJzIHdlJ3JlIHN0YXJ0aW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAjIHdpdGgKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXR5cGUgPSAiRU5TRU1CTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdHlwZSBvZiBnZW5lIGlkZW50aWZpZXIgd2Ugd2FudCByZXR1cm5lZAogICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uID0gIlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBJbiB0aGUgY2FzZSBvZiAxOm1hbnkgbWFwcGluZ3MsIHJldHVybiB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICMgZmlyc3Qgb25lLiBUaGlzIGlzIGRlZmF1bHQgYmVoYXZpb3IhCiAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aVZhbHMgPSAiZmlyc3QiKSAKCiMgV2Ugd291bGQgbGlrZSBhIGRhdGEgZnJhbWUgd2UgY2FuIGpvaW4gdG8gdGhlIERHRSByZXN1bHRzCnN5bWJvbHNfZGYgPC0gZGF0YS5mcmFtZSgKICBlbnNlbWJsX2lkID0gbmFtZXMoc3ltYm9sc192ZWN0b3IpLAogIGdlbmVfc3ltYm9sID0gc3ltYm9sc192ZWN0b3IKKQpgYGAKClRoaXMgbWVzc2FnZSBpcyBsZXR0aW5nIHVzIGtub3cgdGhhdCBzb21ldGltZXMgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzIHdpbGwgbWFwIHRvIG11bHRpcGxlIGdlbmUgc3ltYm9scy4KSW4gdGhpcyBjYXNlLCBpdCdzIGFsc28gcG9zc2libGUgdGhhdCBhIGdlbmUgc3ltYm9sIHdpbGwgbWFwIHRvIG11bHRpcGxlIEVuc2VtYmwgSURzLgoKTm93IHdlIGFyZSByZWFkeSB0byBhZGQgdGhlIGdlbmUgc3ltYm9scyB0byBvdXIgZGF0YSBmcmFtZSB3aXRoIHRoZSBER0UgcmVzdWx0cy4KV2UgY2FuIHVzZSBhIF9qb2luXyBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgdG8gZG8gdGhpcywgd2hpY2ggd2lsbCB1c2UgdGhlIEVuc2VtYmwgZ2VuZSBJRHMgaW4gYm90aCBkYXRhIGZyYW1lcyB0byBkZXRlcm1pbmUgaG93IHRvIGpvaW4gdGhlIHJvd3MuCgpMZXQncyBkbyB0aGlzIGZpcnN0IGZvciB0aGUgY29tcGFyaXNvbiB0byB0aGUgbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uLgoKYGBge3IgYWRkX3N5bWJvbHMsIGxpdmUgPSBUUlVFfQp2c19sb3dfZGYgPC0gc3ltYm9sc19kZiAlPiUKICAjIEFuICppbm5lciogam9pbiB3aWxsIG9ubHkgcmV0dXJuIHJvd3MgdGhhdCBhcmUgaW4gYm90aCBkYXRhIGZyYW1lcwogIGRwbHlyOjppbm5lcl9qb2luKHZzX2xvd19kZiwKICAgICAgICAgICAgICAgICAgICAjIFRoZSBuYW1lIG9mIHRoZSBjb2x1bW4gdGhhdCBjb250YWlucyB0aGUgRW5zZW1ibCBnZW5lIElEcwogICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIGxlZnQgZGF0YSBmcmFtZSBhbmQgcmlnaHQgZGF0YSBmcmFtZQogICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiZW5zZW1ibF9pZCIgPSAiR2VuZSIpKQpgYGAKCiMjIyBEcm9wIGBOQWAgdmFsdWVzCgpTb21lIG9mIHRoZXNlIHJvd3MgaGF2ZSBgTkFgIHZhbHVlcyBpbiBgcGFkamAsIHdoaWNoIFtjYW4gaGFwcGVuIGZvciBhIG51bWJlciBvZiByZWFzb25zXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjcHZhbHVlc05BKSBpbmNsdWRpbmcgd2hlbiBhbGwgc2FtcGxlcyBoYXZlIHplcm8gY291bnRzIG9yIGEgZ2VuZSBoYXMgbG93IG1lYW4gZXhwcmVzc2lvbi4KCkxldCdzIGZpbHRlciB0byByb3dzIHRoYXQgX2RvIG5vdCBoYXZlIGFueSBgTkFgXyB1c2luZyBhIGZ1bmN0aW9uIGB0aWR5cjo6ZHJvcF9uYSgpYC4KVGhpcyB3aWxsIGFsc28gZHJvcCBnZW5lcyB0aGF0IGhhdmUgYW4gRW5zZW1ibCBnZW5lIGlkZW50aWZpZXIgYnV0IG5vIGdlbmUgc3ltYm9sIQoKYGBge3IgY29tcGxldGVfY2FzZXN9CiMgUmVtb3ZlIHJvd3MgdGhhdCBhcmUgbm90IGNvbXBsZXRlIChlLmcuLCBjb250YWluIE5BcykgYnkgZmlsdGVyaW5nIHRvIG9ubHkgCiMgY29tcGxldGUgcm93cwp2c19sb3dfZGYgPC0gdnNfbG93X2RmICU+JQogIHRpZHlyOjpkcm9wX25hKCkKYGBgCgoqKk5vdyB3ZSdsbCByZWFkIGluIG91ciBkYXRhIGZyYW1lIG9mIERHRSByZXN1bHRzIGZyb20gYW5vdGhlciBjb21wYXJpc29uLioqClRvIHNhdmUgdXMgc29tZSB0aW1lIGR1cmluZyBpbnN0cnVjdGlvbiwgd2UndmUgYWxyZWFkeSBkb25lIHRoZSBnZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbiBhbmQgZmlsdGVyaW5nIHRvIHJlbW92ZSBgTkFgIHZhbHVlcyBpbiBbdGhpcyBub3RlYm9va10oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvdHJhaW5pbmctbW9kdWxlcy90cmVlL21hc3Rlci9wYXRod2F5LWFuYWx5c2lzL3NldHVwLzAxLWxldWtlbWlhX0RHRS5SbWQpLiAKV2UgdG9vayBhIGRpZmZlcmVudCBzZXJpZXMgb2Ygc3RlcHMgdG8gYWNoaWV2ZSB0aGUgc2FtZSB0aGluZywgd2hpY2ggaXMgb2Z0ZW4gcG9zc2libGUgaW4gUiEKCgpgYGB7ciByZWFkX2luX3Vuc29ydGVkfQp2c191bnNvcnRlZF9kZiA8LSByZWFkcjo6cmVhZF90c3YodnNfdW5zb3J0ZWRfZmlsZSkKYGBgCgojIyBPdmVyLXJlcHJlc2VudGF0aW9uIEFuYWx5c2lzIChPUkEpCgpUbyB0ZXN0IGZvciBvdmVyLXJlcHJlc2VudGF0aW9uLCB3ZSBjYW4gY2FsY3VsYXRlIGEgcC12YWx1ZSB3aXRoIGEgaHlwZXJnZW9tZXRyaWMgdGVzdCAoW3JlZl0oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2NoYXB0ZXIyLmh0bWwjb3Zlci1yZXByZXNlbnRhdGlvbi1hbmFseXNpcykpLgoKXChwID0gMSAtIFxkaXNwbGF5c3R5bGVcc3VtX3tpID0gMH1ee2stMX1cZnJhY3sge00gXGNob29zZSBpfXsge04tTX0gXGNob29zZSB7bi1pfSB9IH0geyB7TiBcY2hvb3NlIG59IH1cKQoKV2hlcmUgYE5gIGlzIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGJhY2tncm91bmQgZGlzdHJpYnV0aW9uLCBgTWAgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBpbiBhIHBhdGh3YXksIGBuYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHdlIGFyZSBpbnRlcmVzdGVkIGluIChvdXIgbWFya2VyIGdlbmVzKSwgYW5kIGBrYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHRoYXQgb3ZlcmxhcCBiZXR3ZWVuIHRoZSBwYXRod2F5IGFuZCBvdXIgbWFya2VyIGdlbmVzLgoKQm9ycm93aW5nIGFuIGV4YW1wbGUgZnJvbSBbX2NsdXN0ZXJQcm9maWxlcjogdW5pdmVyc2FsIGVucmljaG1lbnQgdG9vbCBmb3IgZnVuY3Rpb25hbCBhbmQgY29tcGFyYXRpdmUgc3R1ZHlfIChZdSApXShodHRwOi8veXVsYWItc211LnRvcC9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI292ZXItcmVwcmVzZW50YXRpb24tYW5hbHlzaXMpOgoKPiAqKkV4YW1wbGUqKjogU3VwcG9zZSB3ZSBoYXZlIDE3LDk4MCBnZW5lcyBkZXRlY3RlZCBpbiBhIE1pY3JvYXJyYXkgc3R1ZHkgYW5kIDU3IGdlbmVzIHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBBbW9uZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3NlZCBnZW5lcywgMjggYXJlIGFubm90YXRlZCB0byBhIGdlbmUgc2V0LgoKV2UnbGwgY2FsbCBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYGdlbmVfaW5faW50ZXJlc3RgIGFuZCBnZW5lcyB0aGF0IGFyZSBpbiB0aGUgZ2VuZSBzZXQgYGluX2dlbmVfc2V0YC4KCmBgYHtyIGdlbmVfdGFibGV9CmdlbmVfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBnZW5lX25vdF9pbnRlcmVzdCA9IGMoMjYxMywgMTUzMTApLAogIGdlbmVfaW5faW50ZXJlc3QgPSBjKDI4LCAyOSkKKQpyb3duYW1lcyhnZW5lX3RhYmxlKSA8LSBjKCJpbl9nZW5lX3NldCIsICJub3RfaW5fZ2VuZV9zZXQiKQoKZ2VuZV90YWJsZQpgYGAKCldlIGNhbiBhc3Nlc3MgaWYgdGhlIDI4IG92ZXJsYXBwaW5nIGdlbmVzIG1lYW4gdGhhdCB0aGUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGFyZSBvdmVyLXJlcHJlc2VudGVkIGluIHRoZSBnZW5lIHNldCB3aXRoIHRoZSBoeXBlcmdlb21ldHJpYyBkaXN0cmlidXRpb24uClRoaXMgY29ycmVzcG9uZHMgdG8gYSBvbmUtc2lkZWQgRmlzaGVyJ3MgZXhhY3QgdGVzdC4KCmBgYHtyIGZpc2hlcl90ZXN0fQpmaXNoZXIudGVzdChnZW5lX3RhYmxlLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikKYGBgCgpXaGVuIHdlIHRlc3QgKiptdWx0aXBsZSBwYXRod2F5cyBvciBnZW5lIHNldHMqKiwgdGhlIHAtdmFsdWVzIHRoZW4gbmVlZCB0byBiZSAqKmFkanVzdGVkKiogZm9yIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZy4KCiMjIyBIaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSBPUkEKCk91ciBER0UgcmVzdWx0cyBhcmUgZnJvbSBkYXRhIHB1Ymxpc2hlZCBhcyBwYXJ0IG9mIFtTYWNocyBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZHguZG9pLm9yZy8xMC4xMTgyJTJGYmxvb2QtMjAxMy0wOC01MjE3MDgpLgpUaGUgYXV0aG9ycyBzb3J0ZWQgcG9wdWxhdGlvbnMgb2YgcHJpbWFyeSBsZXVrZW1pYSBjZWxscyBhbmQgZXhhbWluZWQgdGhlIHN0ZW0gY2VsbCBjYXBhY2l0eSBvZiB0aGVzZSBjZWxsIHBvcHVsYXRpb25zLgooVGhpcyBzdHVkeSBtYXkgc291bmQgZmFtaWxpYXIgaWYgeW91J3ZlIHdvcmtlZCBvbiBvbmUgb2Ygb3VyIGJ1bGsgUk5BLXNlcSBleGVyY2lzZSBub3RlYm9va3MgaW4gdGhlIHBhc3QhKQoKV2UgY29tcGFyZWQgdGhlIHBvcHVsYXRpb24gdGhhdCB0aGUgYXV0aG9ycyBpZGVudGlmaWVkIGFzIGhhdmluZyBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSB0byBhIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbi4KV2UgYWxzbyBjb21wYXJlZCB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgY2VsbHMgdG8gYSBtaXggb2YgcG9wdWxhdGlvbnMgKGUuZy4sIHVuc29ydGVkIGNlbGxzKS4KWW91IGNhbiBzZWUgdGhlIGNvZGUgaW4gW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLW1vZHVsZXMvdHJlZS9tYXN0ZXIvcGF0aHdheS1hbmFseXNpcy9zZXR1cC8wMS1sZXVrZW1pYV9ER0UuUm1kKS4KCldlJ3JlIGludGVyZXN0ZWQgaW4gd2hhdCBwYXRod2F5cyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiBnZW5lcyB0aGF0IHNwZWNpZmljYWxseSBkaXN0aW5ndWlzaCB0aGUgaGlnaCBjYXBhY2l0eSBwb3B1bGF0aW9uIGZyb20gdGhlIGxvdyBjYXBhY2l0eSBwb3B1bGF0aW9uLgoKTGV0J3MgZ2VuZXJhdGUgYSBsaXN0IG9mIGdlbmVzIHRoYXQgaGF2ZSBoaWdoZXIgZXhwcmVzc2lvbiBpbiB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiBjb21wYXJlZCB0byB0aGUgbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uLCBidXQgd2UnbGwgYWxzbyB3YW50IHRvIGV4Y2x1ZGUgZ2VuZXMgdGhhdCBzaG93IHVwIGluIG91ciBfb3RoZXIgY29tcGFyaXNvbl8gdG8gdW5zb3J0ZWQgY2VsbHMuCgpXZSdsbCBzdGFydCB3aXRoIHRoZSBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSB2cy4gbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uIGNvbXBhcmlzb24uCkdlbmVzIHdpdGggcG9zaXRpdmUgbG9nMiBmb2xkLWNoYW5nZXMgKExGQykgd2lsbCBiZSBtb3JlIGhpZ2hseSBleHByZXNzZWQgaW4gdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IGNlbGxzIGJhc2VkIG9uIGhvdyB3ZSBzZXQgdXAgdGhlIGFuYWx5c2lzLgoKYGBge3IgaGlnaF9jYXBhY2l0eV9nZW5lc30KdnNfbG93X2dlbmVzIDwtIHZzX2xvd19kZiAlPiUKICAjIEZpbHRlciB0byB0aGUgcG9zaXRpdmUgTEZDIGFuZCBmaWx0ZXIgYmFzZWQgb24gc2lnbmlmaWNhbmNlIHRvbyAocGFkaikKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSAlPiUKICAjIFJldHVybiBhIHZlY3RvciBvZiBnZW5lIHN5bWJvbHMKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkKYGBgCgpBbHRob3VnaCB3ZSdyZSBwaWNraW5nIGEgX2NvbW1vbmx5IHVzZWRfIGN1dG9mZiAoRkRSIDwgMC4wNSksIGl0J3Mgc3RpbGwgYXJiaXRyYXJ5IGFuZCB3ZSBjb3VsZCBqdXN0IGFzIGVhc2lseSBwaWNrIGEgZGlmZmVyZW50IHRocmVzaG9sZCBmb3Igb3VyIExGQyB2YWx1ZXMuCldoZW4gd2UgZ2VuZXJhdGUgbGlzdHMgb2YgZ2VuZXMgb2YgaW50ZXJlc3QgZm9yIE9SQSwgd2UgdHlwaWNhbGx5IHBpY2sgYW4gYXJiaXRyYXJ5IGN1dG9mZi4KVGhpcyBpcyBvbmUgb2YgdGhlIGFwcHJvYWNoJ3Mgd2Vha25lc3NlcyAtLSB3ZSd2ZSByZW1vdmVkIGFsbCBvdGhlciBjb250ZXh0LgoKTm93LCB3ZSdsbCB0YWtlIHRoZSBzYW1lIHN0ZXBzIGZvciBvdXIgb3RoZXIgcmVzdWx0cy4gCgpgYGB7ciB1bnNvcnRlZF9nZW5lc190b19yZW1vdmUsIGxpdmUgPSBUUlVFfQp2c191bnNvcnRlZF9nZW5lcyA8LSB2c191bnNvcnRlZF9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkKYGBgCgpXZSB3YW50IGdlbmVzIHRoYXQgYXJlIGluIHRoZSBmaXJzdCBjb21wYXJpc29uIGJ1dCBub3QgaW4gdGhlIHNlY29uZCEKV2UgY2FuIHVzZSBgc2V0ZGlmZigpYCwgYSBiYXNlIFIgZnVuY3Rpb24gZm9yIHNldCBvcGVyYXRpb25zLCB0byBnZXQgdGhlIGxpc3QgdGhhdCB3ZSB3YW50LgoKYGBge3Igc2V0ZGlmZn0KIyBXaGF0IGdlbmVzIGFyZSBpbiB0aGUgZmlyc3Qgc2V0IGJ1dCAqbm90KiBpbiB0aGUgc2Vjb25kIHNldApnZW5lc19mb3Jfb3JhIDwtIHNldGRpZmYodnNfbG93X2dlbmVzLCB2c191bnNvcnRlZF9nZW5lcykgCmBgYAoKIyMjIyBCYWNrZ3JvdW5kIHNldAoKQXMgd2Ugc2F3IGFib3ZlLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSByZWxpZXMgb24gdGhlIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgYmFja2dyb3VuZCBkaXN0cmlidXRpb24uClNvbWV0aW1lcyBmb2xrcyBjb25zaWRlciBnZW5lcyBmcm9tIHRoZSBlbnRpcmUgZ2Vub21lIHRvIGNvbXByaXNlIHRoZSBiYWNrZ3JvdW5kLCBidXQgaW4gdGhlIGV4YW1wbGUgYm9ycm93ZWQgZnJvbSB0aGUgYGNsdXN0ZXJQcm9maWxlcmAgYXV0aG9ycywgdGhleSBzdGF0ZToKCj4gMTcsOTgwIGdlbmVzIGRldGVjdGVkIGluIGEgTWljcm9hcnJheSBzdHVkeSAKCldoZXJlIHRoZSBrZXkgcGhyYXNlIGlzICoqZ2VuZXMgZGV0ZWN0ZWQqKi4gCgpJZiB3ZSB3ZXJlIHVuYWJsZSB0byBpbmNsdWRlIGEgZ2VuZSBpbiBvbmUgb2Ygb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zIGJlY2F1c2UsIGZvciBleGFtcGxlLCBpdCBoYWQgbG93IG1lYW4gZXhwcmVzc2lvbiBpbiBvdXIgZXhwZXJpbWVudCBhbmQgdGhlcmVmb3JlIHdhcyBmaWx0ZXJlZCBvdXQgaW4gb3VyIGB0aWR5cjo6ZHJvcF9uYSgpYCBzdGVwLCB3ZSBzaG91bGRuJ3QgaW5jbHVkZSBpbiBvdXIgYmFja2dyb3VuZCBzZXQuICAKCldlIGNhbiB1c2UgYW5vdGhlciBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIGBpbnRlcnNlY3QoKWAsIHRvIGdldCBvdXIgYmFja2dyb3VuZCBzZXQgb2YgZ2VuZXMgdGhhdCB3ZXJlIGluY2x1ZGVkIGluIGJvdGggY29tcGFyaXNvbnMuCgpgYGB7ciBnZXRfYmFja2dyb3VuZF9zZXR9CiMgaW50ZXJzZWN0KCkgd2lsbCByZXR1cm4gdGhlIGdlbmVzIGluIGJvdGggc2V0cyAtIHdlIGFyZSB1c2luZyB0aGUgZW50aXJlIGRhdGEKIyBmcmFtZSBoZXJlIChjb21wbGV0ZSBjYXNlcyksIG5vdCBqdXN0IHRoZSBzaWduaWZpY2FudCBnZW5lcwpiYWNrZ3JvdW5kX3NldCA8LSBpbnRlcnNlY3QodnNfbG93X2RmJGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdnNfdW5zb3J0ZWRfZGYkZ2VuZV9zeW1ib2wpCgojIFJlbW92ZSBhbnl0aGluZyB0aGF0IGNvdWxkbid0IHJlbGlhYmx5IGJlIG1lYXN1cmVkL2Fzc2Vzc2VkIGluIGJvdGggZnJvbSB0aGUKIyBnZW5lcyBvZiBpbnRlcmVzdCBsaXN0IC0gdXNpbmcgaW50ZXJzZWN0KCkgd2lsbCBkcm9wIGFueXRoaW5nIGluIHRoZSBmaXJzdCBzZXQKIyB0aGF0IGlzbid0IGFsc28gaW4gdGhlIHNlY29uZCBzZXQKZ2VuZXNfZm9yX29yYSA8LSBpbnRlcnNlY3QoZ2VuZXNfZm9yX29yYSwgYmFja2dyb3VuZF9zZXQpCmBgYAoKIyMjIyBSdW4gYGVucmljaGVyKClgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBiYWNrZ3JvdW5kIHNldCwgb3VyIGdlbmVzIG9mIGludGVyZXN0LCBhbmQgb3VyIHBhdGh3YXkgaW5mb3JtYXRpb24sIHdlJ3JlIHJlYWR5IHRvIHJ1biBPUkEgdXNpbmcgdGhlIGBlbnJpY2hlcigpYCBmdW5jdGlvbi4KCmBgYHtyIGtlZ2dfb3JhfQprZWdnX29yYV9yZXN1bHRzIDwtIGVucmljaGVyKAogIGdlbmUgPSBnZW5lc19mb3Jfb3JhLCAgIyBHZW5lcyBvZiBpbnRlcmVzdAogIHB2YWx1ZUN1dG9mZiA9IDAuMDUsICAKICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgICMgRkRSCiAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kX3NldCwgICMgQmFja2dyb3VuZCBzZXQKICAjIFRoZSBwYXRod2F5IGluZm9ybWF0aW9uIHNob3VsZCBiZSBhIGRhdGEgZnJhbWUgd2l0aCBhIHRlcm0gbmFtZSBvciAKICAjIGlkZW50aWZpZXIgYW5kIHRoZSBnZW5lIGlkZW50aWZpZXJzCiAgVEVSTTJHRU5FID0gZHBseXI6OnNlbGVjdChtbV9rZWdnX2RmLCAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc19uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zeW1ib2wpCikKYGBgCgoqTm90ZTogdXNpbmcgYGVucmljaEtFR0coKWAgaXMgYSBzaG9ydGN1dCBmb3IgZG9pbmcgT1JBIHVzaW5nIEtFR0csIGJ1dCB0aGUgYXBwcm9hY2ggd2UgY292ZXJlZCBoZXJlIGNhbiBiZSB1c2VkIHdpdGggYW55IGdlbmUgc2V0cyB5b3UnZCBsaWtlISoKCldoYXQgaXMgcmV0dXJuZWQgYnkgYGVucmljaGVyKClgPwoKYGBge3Igdmlld19rZWdnX29yYSwgZXZhbCA9IEZBTFNFfQpWaWV3KGtlZ2dfb3JhX3Jlc3VsdHMpCmBgYAoKVGhlIGluZm9ybWF0aW9uIHdlJ3JlIG1vc3QgbGlrZWx5IGludGVyZXN0ZWQgaW4gaXMgaW4gdGhlIGByZXN1bHRzYCBzbG90LgpMZXQncyBjb252ZXJ0IHRoaXMgaW50byBhIGRhdGEgZnJhbWUgdGhhdCB3ZSBjYW4gd3JpdGUgdG8gZmlsZS4KCmBgYHtyIGtlZ2dfZGZ9CmtlZ2dfcmVzdWx0X2RmIDwtIGRhdGEuZnJhbWUoa2VnZ19vcmFfcmVzdWx0c0ByZXN1bHQpCmBgYAoKIyMjIyBWaXN1YWxpemluZyByZXN1bHRzCgpXZSBjYW4gdXNlIGEgZG90IHBsb3QgdG8gdmlzdWFsaXplIG91ciBzaWduaWZpY2FudCBlbnJpY2htZW50IHJlc3VsdHMuCgpgYGB7ciBkb3RwbG90LCBsaXZlID0gVFJVRX0KZW5yaWNocGxvdDo6ZG90cGxvdChrZWdnX29yYV9yZXN1bHRzKQpgYGAKCldlIGNhbiB1c2UgYW4gW1VwU2V0IHBsb3RdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzQ3MjA5OTMvKSB0byB2aXN1YWxpemUgdGhlICoqb3ZlcmxhcCoqIGJldHdlZW4gdGhlIGdlbmUgc2V0cyB0aGF0IHdlcmUgcmV0dXJuZWQgYXMgc2lnbmlmaWNhbnQuCgpgYGB7ciB1cHNldHBsb3QsIGxpdmUgPSBUUlVFfQplbnJpY2hwbG90Ojp1cHNldHBsb3Qoa2VnZ19vcmFfcmVzdWx0cykKYGBgCgpXZSBjYW4gc2VlIHRoYXQgc29tZSBvZiB0aGUgRE5BIHJlcGFpciBwYXRod2F5cyBzaGFyZSBnZW5lcy4KR2VuZSBzZXRzIG9yIHBhdGh3YXlzIGFyZW4ndCBpbmRlcGVuZGVudCwgZWl0aGVyIQpTb21ldGltZXMgbXVsdGlwbGUgcGF0aHdheXMgdGhhdCBzaG93IHVwIGluIG91ciByZXN1bHRzIGFzIHNpZ25pZmljYW50IGFyZSBpbmRpY2F0aXZlIG9mIG9ubHkgYSBoYW5kZnVsIG9mIGdlbmVzIGluIG91ciBnZW5lIGxpc3QuCgpXZSBjYW4gbG9vayBhdCB0aGUgYGdlbmVJRGAgY29sdW1uIG9mIG91ciByZXN1bHRzIHRvIHNlZSB3aGF0IGdlbmVzIG92ZXJsYXA7IGl0J3MgYSBnb29kIGlkZWEgdG8gdGFrZSBhIGxvb2suCgpgYGB7ciBsb29rX2F0X2dlbmVfaWRzLCBsaXZlID0gVFJVRSwgcm93bmFtZXMucHJpbnQgPSBGQUxTRX0Ka2VnZ19yZXN1bHRfZGYgJT4lCiAgIyBVc2UgZHBseXI6OnNlbGVjdCgpIC0gdGhlIG5hbWUgb2YgdGhlIHBhdGh3YXkgaXMgaW4gdGhlIElEIGNvbHVtbgogIGRwbHlyOjpzZWxlY3QoSUQsIGdlbmVJRCkKYGBgCgojIyMjIFdyaXRlIHJlc3VsdHMgdG8gZmlsZQoKYGBge3Igd3JpdGVfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CnJlYWRyOjp3cml0ZV90c3Yoa2VnZ19yZXN1bHRfZGYsIGZpbGUgPSBrZWdnX3Jlc3VsdHNfZmlsZSkKYGBgCgojIyBTZXNzaW9uIEluZm8gCgpgYGB7ciBzZXNzaW9uX2luZm99CnNlc3Npb25JbmZvKCkKYGBgCg==
    +
    LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogT3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMAotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZ2VuZSBpZGVudGlmaWVyIGNvbnZlcnNpb24gd2l0aCBbYEFubm90YXRpb25EQklgIGFubm90YXRpb24gcGFja2FnZXNdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0Fubm90YXRpb25EYmkvaW5zdC9kb2MvSW50cm9Ub0Fubm90YXRpb25QYWNrYWdlcy5wZGYpCi0gQWNjZXNzIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSBnZW5lIHNldCBjb2xsZWN0aW9uc10oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25zLmpzcCkgdmlhIHRoZSBgbXNpZ2RicmAgcGFja2FnZQotIFByZXBhcmUgZ2VuZSBzZXRzIGZvciBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzLCBpbmNsdWRpbmcgYW4gYXBwcm9wcmlhdGUgYmFja2dyb3VuZCBzZXQKLSBQZXJmb3JtIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgd2l0aCB0aGUgYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZQoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBjb3ZlciBhIHR5cGUgb2YgcGF0aHdheSBvciBnZW5lIHNldCBhbmFseXNpcyBjYWxsZWQgb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKS4KVGhlIGlkZWEgYmVoaW5kIE9SQSBpcyByZWxhdGl2ZWx5IHN0cmFpZ2h0Zm9yd2FyZDogZ2l2ZW4gYSBzZXQgb2YgZ2VuZXMsIGRvIHRoZXNlIGdlbmVzIG92ZXJsYXAgd2l0aCBhIHBhdGh3YXkgbW9yZSB0aGFuIHdlIGV4cGVjdCBieSBjaGFuY2U/ClRoZSBzaW1wbGljaXR5IG9mIG9ubHkgcmVxdWlyaW5nIGFuIGlucHV0IGdlbmUgc2V0IChzb3J0IG9mLCBtb3JlIG9uIHRoYXQgYmVsb3cpIGNhbiBiZSBhdHRyYWN0aXZlLgoKT1JBIGhhcyBzb21lIGxpbWl0YXRpb25zLCBvdXRsaW5lZCBuaWNlbHkgKGFuZCBtb3JlIGV4dGVuc2l2ZWx5ISkgaW4gW0toYXRyaSBfZXQgYWwuXyAoMjAxMildKCBodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMjM3NSkuCk9uZSBvZiB0aGUgbWFpbiBpc3N1ZXMgd2l0aCBPUkEgaXMgdGhhdCB0eXBpY2FsbHkgYWxsIGdlbmVzIGFyZSB0cmVhdGVkIGFzIGVxdWFsIC0tIHRoZSBjb250ZXh0IG9mIHRoZSBtYWduaXR1ZGUgb2YgYSBjaGFuZ2Ugd2UgbWF5IGJlIG1lYXN1cmluZyBpcyByZW1vdmVkIGFuZCBlYWNoIGdlbmUgaXMgdHJlYXRlZCBhcyBpbmRlcGVuZGVudCwgd2hpY2ggY2FuIHNvbWV0aW1lcyByZXN1bHQgaW4gYW4gaW5jb3JyZWN0IGVzdGltYXRlIG9mIHNpZ25pZmljYW5jZS4KCldlIHdpbGwgdXNlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2NsdXN0ZXJQcm9maWxlci5odG1sKSAoW1l1ICpldCBhbC4qIDIwMTJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDg5L29taS4yMDExLjAxMTguKSkgdG8gcGVyZm9ybSBPUkEuCmBjbHVzdGVyUHJvZmlsZXJgIGhhcyBtYW55IGJ1aWx0LWluIGZ1bmN0aW9ucyB0aGF0IHdpbGwgcnVuIGEgc3BlY2lmaWMgdHlwZSBvZiBhbmFseXNpcyB1c2luZyBhIHNwZWNpZmljIHNvdXJjZSBvZiBwYXRod2F5cy9nZW5lIHNldHMgYXV0b21hdGljYWxseSwgYnV0IGZvciBvdXIgcHVycG9zZXMgd2UncmUgZ29pbmcgdG8ga2VlcCB0aGluZ3MgYXMgZ2VuZXJhbCBhcyBwb3NzaWJsZS4KU2VlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgYm9va10oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2luZGV4Lmh0bWwpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwYWNrYWdlJ3MgZnVsbCBzdWl0ZSBvZiBmdW5jdGlvbmFsaXR5LgoKQmVjYXVzZSBkaWZmZXJlbnQgYmlvaW5mb3JtYXRpY3MgdG9vbHMgb2Z0ZW4gcmVxdWlyZSBkaWZmZXJlbnQgdHlwZXMgb2YgZ2VuZSBpZGVudGlmaWVycywgd2UnbGwgYWxzbyBjb3ZlciBob3cgdG8gY29udmVydCBiZXR3ZWVuIGdlbmUgaWRlbnRpZmllcnMgdXNpbmcgW2BBbm5vdGF0aW9uRGJpYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0Fubm90YXRpb25EYmkuaHRtbCkgQmlvY29uZHVjdG9yIHBhY2thZ2VzIGluIHRoaXMgbm90ZWJvb2suCkNoZWNrIG91dCB0aGUgW19Bbm5vdGF0aW9uRGJpOiBJbnRyb2R1Y3Rpb24gVG8gQmlvY29uZHVjdG9yIEFubm90YXRpb24gUGFja2FnZXNfIChDYXJsc29uIDIwMjAuKSB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvQW5ub3RhdGlvbkRiaS9pbnN0L2RvYy9JbnRyb1RvQW5ub3RhdGlvblBhY2thZ2VzLnBkZikgZm9yIG1vcmUgaW5mb3JtYXRpb24uCgojIyMjIE90aGVyIHJlc291cmNlcwoKKiBGb3IgYW5vdGhlciBleGFtcGxlIHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgLCBzZWUgW19JbnRybyB0byBER0U6IEZ1bmN0aW9uYWwgQW5hbHlzaXMuXyBmcm9tIEhhcnZhcmQgQ2hhbiBCaW9pbmZvcm1hdGljcyBDb3JlIFRyYWluaW5nLl0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDlfZnVuY3Rpb25hbF9hbmFseXNpcy5odG1sKQoqIFtgV2ViR2VzdGFsdFJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvV2ViR2VzdGFsdFIvKSBpcyBhbm90aGVyIFIgcGFja2FnZSB0aGF0IGNhbiBiZSB1c2VkIGZvciBPUkEuCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMKCmBgYHtyIGxpYnJhcmllc30KIyBQYWNrYWdlIHdlJ2xsIHVzZSBmb3IgT1JBCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQojIFBhY2thZ2UgdGhhdCBjb250YWlucyBNU2lnREIgZ2VuZSBzZXRzIGluIHRpZHkgZm9ybWF0CmxpYnJhcnkobXNpZ2RicikKIyBNdXMgbXVzY3VsdXMgYW5ub3RhdGlvbiBwYWNrYWdlIHdlJ2xsIHVzZSBmb3IgZ2VuZSBpZGVudGlmaWVyIGNvbnZlcnNpb24KbGlicmFyeShvcmcuTW0uZWcuZGIpCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKIyMjIyBEaXJlY3RvcmllcwoKYGBge3IgY3JlYXRlX29yYV9kaXJlY3RvcnksIGxpdmUgPSBUUlVFfQojIFdlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBzcGVjaWZpY2FsbHkgaG9sZCB0aGUgT1JBIHJlc3VsdHMgaWYgaXQgZG9lc24ndAojIGV4aXN0IHlldApyZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAibGV1a2VtaWEiKQppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCiMjIyMgSW5wdXQgZmlsZXMKCkZvciBvdXIgT1JBIGV4YW1wbGUsIHdlJ3JlIGdvaW5nIHRvIHVzZSB0d28gdGFibGVzIG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gKERHRSkgYW5hbHlzaXMgcmVzdWx0cy4KCmBgYHtyIGlucHV0X2RpcmVjdG9yeX0KaW5wdXRfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJsZXVrZW1pYSIpCgojIFRoaXMgZmlsZSBjb250YWlucyB0aGUgREdFIHJlc3VsdHMgZm9yIGEgY2VsbCBwb3B1bGF0aW9uIHdpdGggaGlnaCBzdGVtIGNlbGwKIyBjYXBhY2l0eSBhcyBjb21wYXJlZCB0byBhIGNlbGwgcG9wdWxhdGlvbgp2c19sb3dfZmlsZSA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hfY2FwYWNpdHlfdnNfbG93X2NhcGFjaXR5X3Jlc3VsdHMudHN2IikKCiMgVGhpcyBmaWxlIGNvbnRhaW5zIHRoZSBER0UgcmVzdWx0cyBmb3IgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24KIyB2cy4gdW5zb3J0ZWQgY2VsbHMgKGUuZy4sIGEgbWl4dHVyZSBvZiBjYXBhY2l0aWVzKQp2c191bnNvcnRlZF9maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoX2NhcGFjaXR5X3ZzX3Vuc29ydGVkX3Jlc3VsdHMudHN2IikKYGBgCgojIyMjIE91dHB1dCBmaWxlcwoKV2UnbGwgc2F2ZSB0aGUgdGFibGUgb2YgT1JBIHJlc3VsdHMgKGUuZy4sIHAtdmFsdWVzKS4KCmBgYHtyIG91dHB1dF9maWxlLCBsaXZlID0gVFJVRX0Ka2VnZ19yZXN1bHRzX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAibGV1a2VtaWFfa2VnZ19vcmFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgc2V0cwoKV2Ugd2lsbCB1c2UgZ2VuZSBzZXRzIGZyb20gdGhlIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKV0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2luZGV4LmpzcCkgZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlIChbU3VicmFtYW5pYW4sIFRhbWF5byAqZXQgYWwuKiAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpKS4KVGhlIFtgbXNpZ2RicmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tc2lnZGJyL2luZGV4Lmh0bWwpIHBhY2thZ2UgY29udGFpbnMgTVNpZ0RCIGRhdGFzZXRzIGFscmVhZHkgaW4gdGhlIHRpZHkgZm9ybWF0IHJlcXVpcmVkIGJ5IGBjbHVzdGVyUHJvZmlsZXJgIGFuZCBzdXBwb3J0cyBtdWx0aXBsZSBvcmdhbmlzbXMuCgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IG9yZ2FuaXNtcyB0aGUgcGFja2FnZSBzdXBwb3J0cy4KCmBgYHtyIHNob3dfc3BlY2llc30KbXNpZ2Ricl9zcGVjaWVzKCkKYGBgCgpUaGUgcmVzdWx0cyB3ZSdyZSBpbnRlcmVzdGVkIGluIGhlcmUgY29tZSBmcm9tIG1vdXNlIHNhbXBsZXMsIHNvIHdlIGNhbiBvYnRhaW4ganVzdCB0aGUgZ2VuZSBzZXRzIHJlbGV2YW50IHRvIF9NLiBtdXNjdWx1c18gd2l0aCB0aGUgYHNwZWNpZXNgIGFyZ3VtZW50IHRvIGBtc2lnZGJyKClgLgoKYGBge3IgbW1fZGYsIGxpdmUgPSBUUlVFfQptbV9tc2lnZGJfZGYgPC0gbXNpZ2RicihzcGVjaWVzID0gIk11cyBtdXNjdWx1cyIpCmBgYAoKTVNpZ0RCIGNvbnRhaW5zIDggZGlmZmVyZW50IGdlbmUgc2V0IGNvbGxlY3Rpb25zLgoKICAgIEg6IGhhbGxtYXJrIGdlbmUgc2V0cwogICAgQzE6IHBvc2l0aW9uYWwgZ2VuZSBzZXRzCiAgICBDMjogY3VyYXRlZCBnZW5lIHNldHMKICAgIEMzOiBtb3RpZiBnZW5lIHNldHMKICAgIEM0OiBjb21wdXRhdGlvbmFsIGdlbmUgc2V0cwogICAgQzU6IEdPIGdlbmUgc2V0cwogICAgQzY6IG9uY29nZW5pYyBzaWduYXR1cmVzCiAgICBDNzogaW1tdW5vbG9naWMgc2lnbmF0dXJlcwoKSW4gdGhpcyBleGFtcGxlLCB3ZSB3aWxsIHVzZSBjYW5vbmljYWwgcGF0aHdheXMgd2hpY2ggYXJlIChbcmVmXShodHRwczovL3d3dy5nc2VhLW1zaWdkYi5vcmcvZ3NlYS9tc2lnZGIvY29sbGVjdGlvbnMuanNwKSk6Cgo+IEdlbmUgc2V0cyBmcm9tIHBhdGh3YXkgZGF0YWJhc2VzLiBVc3VhbGx5LCB0aGVzZSBnZW5lIHNldHMgYXJlIGNhbm9uaWNhbCByZXByZXNlbnRhdGlvbnMgb2YgYSBiaW9sb2dpY2FsIHByb2Nlc3MgY29tcGlsZWQgYnkgZG9tYWluIGV4cGVydHMuCgpBbmQgYXJlIGEgc3Vic2V0IG9mIGBDMjogY3VyYXRlZCBnZW5lIHNldHNgLgpTcGVjaWZpY2FsbHksIHdlIHdpbGwgdXNlIHRoZSBbS0VHRyAoS3lvdG8gRW5jeWNsb3BlZGlhIG9mIEdlbmVzIGFuZCBHZW5vbWVzKV0oaHR0cHM6Ly93d3cuZ2Vub21lLmpwL2tlZ2cvKSBwYXRod2F5cy4KCmBgYHtyIGZpbHRlcl90b19rZWdnfQojIEZpbHRlciB0aGUgbW91c2UgZGF0YSBmcmFtZSB0byB0aGUgS0VHRyBwYXRod2F5cyB0aGF0IGFyZSBpbmNsdWRlZCBpbiB0aGUKIyBjdXJhdGVkIGdlbmUgc2V0cwptbV9rZWdnX2RmIDwtIG1tX21zaWdkYl9kZiB8PgogIGRwbHlyOjpmaWx0ZXIoZ3NfY2F0ID09ICJDMiIsICAjIGN1cmF0ZWQgZ2VuZSBzZXRzCiAgICAgICAgICAgICAgICBnc19zdWJjYXQgPT0gIkNQOktFR0ciKSAgIyBLRUdHIHBhdGh3YXlzCmBgYAoKKk5vdGU6IFdlIGNvdWxkIHNwZWNpZmllZCB0aGF0IHdlIHdhbnRlZCB0aGUgS0VHRyBnZW5lIHNldHMgdXNpbmcgdGhlIGBjYXRlZ29yeWAgYW5kIGBzdWJjYXRlZ29yeWAgYXJndW1lbnRzIG9mIGBtc2lnZGJyKClgLCBidXQgd2UncmUgZ29pbmcgZm9yIGdlbmVyYWwgc3RlcHMhKgoKYGBge3IgbW1fa2VnZ19jb2x1bW5zfQpjb2xuYW1lcyhtbV9rZWdnX2RmKQpgYGAKClRoZSBgY2x1c3RlclByb2ZpbGVyYCBmdW5jdGlvbiB3ZSB3aWxsIHVzZSByZXF1aXJlcyBhIGRhdGEgZnJhbWUgd2l0aCB0d28gY29sdW1ucywgd2hlcmUgb25lIGNvbHVtbiBjb250YWlucyB0aGUgdGVybSBpZGVudGlmaWVyIG9yIG5hbWUgYW5kIG9uZSBjb2x1bW4gY29udGFpbnMgZ2VuZSBpZGVudGlmaWVycyB0aGF0IG1hdGNoIG91ciBnZW5lIGxpc3RzIHdlIHdhbnQgdG8gY2hlY2sgZm9yIGVucmljaG1lbnQuCk91ciBkYXRhIGZyYW1lIHdpdGggS0VHRyB0ZXJtcyBjb250YWlucyBFbnRyZXogSURzIGFuZCBnZW5lIHN5bWJvbHMuCgojIyBSZWFkIGluIERHRSByZXN1bHRzIGFuZCBwcmVwCgpgYGB7ciByZWFkX2luX2RnZV9yZXN1bHRzLCBsaXZlID0gVFJVRX0KdnNfbG93X2RmIDwtIHJlYWRyOjpyZWFkX3Rzdih2c19sb3dfZmlsZSkKYGBgCgpMZXQncyB0YWtlIGEgcGVlayBhdCB0aGUgdG9wIG9mIHRoZSBER0UgcmVzdWx0cyBkYXRhIGZyYW1lLgoKYGBge3IgcGVla19hdF9kZ2VfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CmhlYWQodnNfbG93X2RmKQpgYGAKCiMjIyBHZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbgoKT3VyIGRhdGEgZnJhbWUgb2YgREdFIHJlc3VsdHMgY29udGFpbnMgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLgpTbyB3ZSB3aWxsIG5lZWQgdG8gY29udmVydCBmcm9tIHRoZXNlIGlkZW50aWZpZXJzIGludG8gZWl0aGVyIHRoZSBnZW5lIHN5bWJvbHMgb3IgRW50cmV6IElEcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBkYXRhIHdlIGV4dHJhY3RlZCB3aXRoIGBtc2lnZGJyKClgLgoKV2UncmUgZ29pbmcgdG8gY29udmVydCBvdXIgaWRlbnRpZmllcnMgdG8gZ2VuZSBzeW1ib2xzIGJlY2F1c2UgdGhleSBhcmUgYSBiaXQgbW9yZSBodW1hbiByZWFkYWJsZSwgYnV0IHlvdSBjYW4sIHdpdGggdGhlIGNoYW5nZSBvZiBhIHNpbmdsZSBhcmd1bWVudCwgdXNlIHRoZSBzYW1lIGNvZGUgdG8gY29udmVydCB0byBtYW55IG90aGVyIHR5cGVzIG9mIGlkZW50aWZpZXJzIQoKVGhlIGFubm90YXRpb24gcGFja2FnZSBgb3JnLk1tLmVnLmRiYCBjb250YWlucyBpbmZvcm1hdGlvbiBmb3IgZGlmZmVyZW50IGlkZW50aWZpZXJzLgpgb3JnLk1tLmVnLmRiYCBpcyBzcGVjaWZpYyB0byBfTXVzIG11c2N1bHVzXyAtLSB0aGlzIGlzIHdoYXQgdGhlIGBNbWAgaW4gdGhlIHBhY2thZ2UgbmFtZSBpcyByZWZlcmVuY2luZy4KVG8gcGVyZm9ybSBnZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbiBpbiBodW1hbiAoX0hvbW8gc2FwaWVuc18pIHdlIGNvdWxkIHVzZSBgb3JnLkhzLmVnLmRiYDsKd2Ugd291bGQgdXNlIGBvcmcuRHIuZWcuZGJgIGZvciB6ZWJyYWZpc2ggKF9EYW5pbyByZXJpb18pLgoKV2UgY2FuIHNlZSB3aGF0IHR5cGVzIG9mIElEcyBhcmUgYXZhaWxhYmxlIHRvIHVzIGluIGFuIGFubm90YXRpb24gcGFja2FnZSB3aXRoIGBrZXl0eXBlcygpYC4KCmBgYHtyIGtleXR5cGVzLCBsaXZlID0gVFJVRX0Ka2V5dHlwZXMob3JnLk1tLmVnLmRiKQpgYGAKCkV2ZW4gdGhvdWdoIHdlJ2xsIHVzZSB0aGlzIHBhY2thZ2UgdG8gY29udmVydCBmcm9tIEVuc2VtYmwgZ2VuZSBJRHMgKGBFTlNFTUJMYCkgdG8gZ2VuZSBzeW1ib2xzIChgU1lNQk9MYCksIHdlIGNvdWxkIGp1c3QgYXMgZWFzaWx5IHVzZSBpdCB0byBjb252ZXJ0IGZyb20gYW4gRW5zZW1ibCB0cmFuc2NyaXB0IElEIChgRU5TRU1CTFRSQU5TYCkgdG8gRW50cmV6IElEcyAoYEVOVFJFWklEYCkuCgpUaGUgZnVuY3Rpb24gd2Ugd2lsbCB1c2UgdG8gbWFwIGZyb20gRW5zZW1ibCBnZW5lIElEcyB0byBnZW5lIHN5bWJvbHMgaXMgY2FsbGVkIGBtYXBJZHMoKWAuCgpgYGB7ciBtYXBfdG9fc3ltYm9sfQojIFRoaXMgcmV0dXJucyBhIG5hbWVkIHZlY3RvciB3aGljaCB3ZSBjYW4gY29udmVydCB0byBhIGRhdGEgZnJhbWUsIHdoZXJlCiMgdGhlIGtleXMgKEVuc2VtYmwgSURzKSBhcmUgdGhlIG5hbWVzCnN5bWJvbHNfdmVjdG9yIDwtIG1hcElkcyhvcmcuTW0uZWcuZGIsICAjIFNwZWNpZnkgdGhlIGFubm90YXRpb24gcGFja2FnZQogICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdmVjdG9yIG9mIGdlbmUgaWRlbnRpZmllcnMgd2Ugd2FudCB0bwogICAgICAgICAgICAgICAgICAgICAgICAgIyBtYXAKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXMgPSB2c19sb3dfZGYkR2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICMgV2hhdCB0eXBlIG9mIGdlbmUgaWRlbnRpZmllcnMgd2UncmUgc3RhcnRpbmcKICAgICAgICAgICAgICAgICAgICAgICAgICMgd2l0aAogICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJFTlNFTUJMIiwKICAgICAgICAgICAgICAgICAgICAgICAgICMgVGhlIHR5cGUgb2YgZ2VuZSBpZGVudGlmaWVyIHdlIHdhbnQgcmV0dXJuZWQKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbiA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgIyBJbiB0aGUgY2FzZSBvZiAxOm1hbnkgbWFwcGluZ3MsIHJldHVybiB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICMgZmlyc3Qgb25lLiBUaGlzIGlzIGRlZmF1bHQgYmVoYXZpb3IhCiAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aVZhbHMgPSAiZmlyc3QiKQoKIyBXZSB3b3VsZCBsaWtlIGEgZGF0YSBmcmFtZSB3ZSBjYW4gam9pbiB0byB0aGUgREdFIHJlc3VsdHMKc3ltYm9sc19kZiA8LSBkYXRhLmZyYW1lKAogIGVuc2VtYmxfaWQgPSBuYW1lcyhzeW1ib2xzX3ZlY3RvciksCiAgZ2VuZV9zeW1ib2wgPSBzeW1ib2xzX3ZlY3RvcgopCmBgYAoKVGhpcyBtZXNzYWdlIGlzIGxldHRpbmcgdXMga25vdyB0aGF0IHNvbWV0aW1lcyBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMgd2lsbCBtYXAgdG8gbXVsdGlwbGUgZ2VuZSBzeW1ib2xzLgpJbiB0aGlzIGNhc2UsIGl0J3MgYWxzbyBwb3NzaWJsZSB0aGF0IGEgZ2VuZSBzeW1ib2wgd2lsbCBtYXAgdG8gbXVsdGlwbGUgRW5zZW1ibCBJRHMuCgpOb3cgd2UgYXJlIHJlYWR5IHRvIGFkZCB0aGUgZ2VuZSBzeW1ib2xzIHRvIG91ciBkYXRhIGZyYW1lIHdpdGggdGhlIERHRSByZXN1bHRzLgpXZSBjYW4gdXNlIGEgX2pvaW5fIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSB0byBkbyB0aGlzLCB3aGljaCB3aWxsIHVzZSB0aGUgRW5zZW1ibCBnZW5lIElEcyBpbiBib3RoIGRhdGEgZnJhbWVzIHRvIGRldGVybWluZSBob3cgdG8gam9pbiB0aGUgcm93cy4KCkxldCdzIGRvIHRoaXMgZmlyc3QgZm9yIHRoZSBjb21wYXJpc29uIHRvIHRoZSBsb3cgc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24uCgpgYGB7ciBhZGRfc3ltYm9scywgbGl2ZSA9IFRSVUV9CnZzX2xvd19kZiA8LSBzeW1ib2xzX2RmIHw+CiAgIyBBbiAqaW5uZXIqIGpvaW4gd2lsbCBvbmx5IHJldHVybiByb3dzIHRoYXQgYXJlIGluIGJvdGggZGF0YSBmcmFtZXMKICBkcGx5cjo6aW5uZXJfam9pbih2c19sb3dfZGYsCiAgICAgICAgICAgICAgICAgICAgIyBUaGUgbmFtZSBvZiB0aGUgY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIEVuc2VtYmwgZ2VuZSBJRHMKICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBsZWZ0IGRhdGEgZnJhbWUgYW5kIHJpZ2h0IGRhdGEgZnJhbWUKICAgICAgICAgICAgICAgICAgICBieSA9IGMoImVuc2VtYmxfaWQiID0gIkdlbmUiKSkKYGBgCgojIyMgRHJvcCBgTkFgIHZhbHVlcwoKU29tZSBvZiB0aGVzZSByb3dzIGhhdmUgYE5BYCB2YWx1ZXMgaW4gYHBhZGpgLCB3aGljaCBbY2FuIGhhcHBlbiBmb3IgYSBudW1iZXIgb2YgcmVhc29uc10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3B2YWx1ZXNOQSkgaW5jbHVkaW5nIHdoZW4gYWxsIHNhbXBsZXMgaGF2ZSB6ZXJvIGNvdW50cyBvciBhIGdlbmUgaGFzIGxvdyBtZWFuIGV4cHJlc3Npb24uCgpMZXQncyBmaWx0ZXIgdG8gcm93cyB0aGF0IF9kbyBub3QgaGF2ZSBhbnkgYE5BYF8gdXNpbmcgYSBmdW5jdGlvbiBgdGlkeXI6OmRyb3BfbmEoKWAuClRoaXMgd2lsbCBhbHNvIGRyb3AgZ2VuZXMgdGhhdCBoYXZlIGFuIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVyIGJ1dCBubyBnZW5lIHN5bWJvbCEKCmBgYHtyIGNvbXBsZXRlX2Nhc2VzfQojIFJlbW92ZSByb3dzIHRoYXQgYXJlIG5vdCBjb21wbGV0ZSAoZS5nLiwgY29udGFpbiBOQXMpIGJ5IGZpbHRlcmluZyB0byBvbmx5CiMgY29tcGxldGUgcm93cwp2c19sb3dfZGYgPC0gdnNfbG93X2RmIHw+CiAgdGlkeXI6OmRyb3BfbmEoKQpgYGAKCioqTm93IHdlJ2xsIHJlYWQgaW4gb3VyIGRhdGEgZnJhbWUgb2YgREdFIHJlc3VsdHMgZnJvbSBhbm90aGVyIGNvbXBhcmlzb24uKioKVG8gc2F2ZSB1cyBzb21lIHRpbWUgZHVyaW5nIGluc3RydWN0aW9uLCB3ZSd2ZSBhbHJlYWR5IGRvbmUgdGhlIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIGFuZCBmaWx0ZXJpbmcgdG8gcmVtb3ZlIGBOQWAgdmFsdWVzIGluIFt0aGlzIG5vdGVib29rXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL3BhdGh3YXktYW5hbHlzaXMvc2V0dXAvMDEtbGV1a2VtaWFfREdFLlJtZCkuCldlIHRvb2sgYSBkaWZmZXJlbnQgc2VyaWVzIG9mIHN0ZXBzIHRvIGFjaGlldmUgdGhlIHNhbWUgdGhpbmcsIHdoaWNoIGlzIG9mdGVuIHBvc3NpYmxlIGluIFIhCgoKYGBge3IgcmVhZF9pbl91bnNvcnRlZH0KdnNfdW5zb3J0ZWRfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZzX3Vuc29ydGVkX2ZpbGUpCmBgYAoKIyMgT3Zlci1yZXByZXNlbnRhdGlvbiBBbmFseXNpcyAoT1JBKQoKVG8gdGVzdCBmb3Igb3Zlci1yZXByZXNlbnRhdGlvbiwgd2UgY2FuIGNhbGN1bGF0ZSBhIHAtdmFsdWUgd2l0aCBhIGh5cGVyZ2VvbWV0cmljIHRlc3QgKFtyZWZdKGh0dHBzOi8veXVsYWItc211LmdpdGh1Yi5pby9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI292ZXItcmVwcmVzZW50YXRpb24tYW5hbHlzaXMpKS4KClwocCA9IDEgLSBcZGlzcGxheXN0eWxlXHN1bV97aSA9IDB9XntrLTF9XGZyYWN7IHtNIFxjaG9vc2UgaX17IHtOLU19IFxjaG9vc2Uge24taX0gfSB9IHsge04gXGNob29zZSBufSB9XCkKCldoZXJlIGBOYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBiYWNrZ3JvdW5kIGRpc3RyaWJ1dGlvbiwgYE1gIGlzIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gYSBwYXRod2F5LCBgbmAgaXMgdGhlIG51bWJlciBvZiBnZW5lcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiAob3VyIG1hcmtlciBnZW5lcyksIGFuZCBga2AgaXMgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IG92ZXJsYXAgYmV0d2VlbiB0aGUgcGF0aHdheSBhbmQgb3VyIG1hcmtlciBnZW5lcy4KCkJvcnJvd2luZyBhbiBleGFtcGxlIGZyb20gW19jbHVzdGVyUHJvZmlsZXI6IHVuaXZlcnNhbCBlbnJpY2htZW50IHRvb2wgZm9yIGZ1bmN0aW9uYWwgYW5kIGNvbXBhcmF0aXZlIHN0dWR5XyAoWXUgKV0oaHR0cDovL3l1bGFiLXNtdS50b3AvY2x1c3RlclByb2ZpbGVyLWJvb2svY2hhcHRlcjIuaHRtbCNvdmVyLXJlcHJlc2VudGF0aW9uLWFuYWx5c2lzKToKCj4gKipFeGFtcGxlKio6IFN1cHBvc2Ugd2UgaGF2ZSAxNyw5ODAgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBNaWNyb2FycmF5IHN0dWR5IGFuZCA1NyBnZW5lcyB3ZXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4gQW1vbmcgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMsIDI4IGFyZSBhbm5vdGF0ZWQgdG8gYSBnZW5lIHNldC4KCldlJ2xsIGNhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGBnZW5lX2luX2ludGVyZXN0YCBhbmQgZ2VuZXMgdGhhdCBhcmUgaW4gdGhlIGdlbmUgc2V0IGBpbl9nZW5lX3NldGAuCgpgYGB7ciBnZW5lX3RhYmxlfQpnZW5lX3RhYmxlIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZV9ub3RfaW50ZXJlc3QgPSBjKDI2MTMsIDE1MzEwKSwKICBnZW5lX2luX2ludGVyZXN0ID0gYygyOCwgMjkpCikKcm93bmFtZXMoZ2VuZV90YWJsZSkgPC0gYygiaW5fZ2VuZV9zZXQiLCAibm90X2luX2dlbmVfc2V0IikKCmdlbmVfdGFibGUKYGBgCgpXZSBjYW4gYXNzZXNzIGlmIHRoZSAyOCBvdmVybGFwcGluZyBnZW5lcyBtZWFuIHRoYXQgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiB0aGUgZ2VuZSBzZXQgd2l0aCB0aGUgaHlwZXJnZW9tZXRyaWMgZGlzdHJpYnV0aW9uLgpUaGlzIGNvcnJlc3BvbmRzIHRvIGEgb25lLXNpZGVkIEZpc2hlcidzIGV4YWN0IHRlc3QuCgpgYGB7ciBmaXNoZXJfdGVzdH0KZmlzaGVyLnRlc3QoZ2VuZV90YWJsZSwgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCmBgYAoKV2hlbiB3ZSB0ZXN0ICoqbXVsdGlwbGUgcGF0aHdheXMgb3IgZ2VuZSBzZXRzKiosIHRoZSBwLXZhbHVlcyB0aGVuIG5lZWQgdG8gYmUgKiphZGp1c3RlZCoqIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcuCgojIyMgSGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgT1JBCgpPdXIgREdFIHJlc3VsdHMgYXJlIGZyb20gZGF0YSBwdWJsaXNoZWQgYXMgcGFydCBvZiBbU2FjaHMgX2V0IGFsLl8gKDIwMTQpXShodHRwczovL2R4LmRvaS5vcmcvMTAuMTE4MiUyRmJsb29kLTIwMTMtMDgtNTIxNzA4KS4KVGhlIGF1dGhvcnMgc29ydGVkIHBvcHVsYXRpb25zIG9mIHByaW1hcnkgbGV1a2VtaWEgY2VsbHMgYW5kIGV4YW1pbmVkIHRoZSBzdGVtIGNlbGwgY2FwYWNpdHkgb2YgdGhlc2UgY2VsbCBwb3B1bGF0aW9ucy4KKFRoaXMgc3R1ZHkgbWF5IHNvdW5kIGZhbWlsaWFyIGlmIHlvdSd2ZSB3b3JrZWQgb24gb25lIG9mIG91ciBidWxrIFJOQS1zZXEgZXhlcmNpc2Ugbm90ZWJvb2tzIGluIHRoZSBwYXN0ISkKCldlIGNvbXBhcmVkIHRoZSBwb3B1bGF0aW9uIHRoYXQgdGhlIGF1dGhvcnMgaWRlbnRpZmllZCBhcyBoYXZpbmcgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgdG8gYSBsb3cgc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24uCldlIGFsc28gY29tcGFyZWQgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IGNlbGxzIHRvIGEgbWl4IG9mIHBvcHVsYXRpb25zIChlLmcuLCB1bnNvcnRlZCBjZWxscykuCllvdSBjYW4gc2VlIHRoZSBjb2RlIGluIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL3BhdGh3YXktYW5hbHlzaXMvc2V0dXAvMDEtbGV1a2VtaWFfREdFLlJtZCkuCgpXZSdyZSBpbnRlcmVzdGVkIGluIHdoYXQgcGF0aHdheXMgYXJlIG92ZXItcmVwcmVzZW50ZWQgaW4gZ2VuZXMgdGhhdCBzcGVjaWZpY2FsbHkgZGlzdGluZ3Vpc2ggdGhlIGhpZ2ggY2FwYWNpdHkgcG9wdWxhdGlvbiBmcm9tIHRoZSBsb3cgY2FwYWNpdHkgcG9wdWxhdGlvbi4KCkxldCdzIGdlbmVyYXRlIGEgbGlzdCBvZiBnZW5lcyB0aGF0IGhhdmUgaGlnaGVyIGV4cHJlc3Npb24gaW4gdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24gY29tcGFyZWQgdG8gdGhlIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiwgYnV0IHdlJ2xsIGFsc28gd2FudCB0byBleGNsdWRlIGdlbmVzIHRoYXQgc2hvdyB1cCBpbiBvdXIgX290aGVyIGNvbXBhcmlzb25fIHRvIHVuc29ydGVkIGNlbGxzLgoKV2UnbGwgc3RhcnQgd2l0aCB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgdnMuIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiBjb21wYXJpc29uLgpHZW5lcyB3aXRoIHBvc2l0aXZlIGxvZzIgZm9sZC1jaGFuZ2VzIChMRkMpIHdpbGwgYmUgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIHRoZSBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSBjZWxscyBiYXNlZCBvbiBob3cgd2Ugc2V0IHVwIHRoZSBhbmFseXNpcy4KCmBgYHtyIGhpZ2hfY2FwYWNpdHlfZ2VuZXN9CnZzX2xvd19nZW5lcyA8LSB2c19sb3dfZGYgfD4KICAjIEZpbHRlciB0byB0aGUgcG9zaXRpdmUgTEZDIGFuZCBmaWx0ZXIgYmFzZWQgb24gc2lnbmlmaWNhbmNlIHRvbyAocGFkaikKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSB8PgogICMgUmV0dXJuIGEgdmVjdG9yIG9mIGdlbmUgc3ltYm9scwogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKQpgYGAKCkFsdGhvdWdoIHdlJ3JlIHBpY2tpbmcgYSBfY29tbW9ubHkgdXNlZF8gY3V0b2ZmIChGRFIgPCAwLjA1KSwgaXQncyBzdGlsbCBhcmJpdHJhcnkgYW5kIHdlIGNvdWxkIGp1c3QgYXMgZWFzaWx5IHBpY2sgYSBkaWZmZXJlbnQgdGhyZXNob2xkIGZvciBvdXIgTEZDIHZhbHVlcy4KV2hlbiB3ZSBnZW5lcmF0ZSBsaXN0cyBvZiBnZW5lcyBvZiBpbnRlcmVzdCBmb3IgT1JBLCB3ZSB0eXBpY2FsbHkgcGljayBhbiBhcmJpdHJhcnkgY3V0b2ZmLgpUaGlzIGlzIG9uZSBvZiB0aGUgYXBwcm9hY2gncyB3ZWFrbmVzc2VzIC0tIHdlJ3ZlIHJlbW92ZWQgYWxsIG90aGVyIGNvbnRleHQuCgpOb3csIHdlJ2xsIHRha2UgdGhlIHNhbWUgc3RlcHMgZm9yIG91ciBvdGhlciByZXN1bHRzLgoKYGBge3IgdW5zb3J0ZWRfZ2VuZXNfdG9fcmVtb3ZlLCBsaXZlID0gVFJVRX0KdnNfdW5zb3J0ZWRfZ2VuZXMgPC0gdnNfdW5zb3J0ZWRfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSB8PgogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKQpgYGAKCldlIHdhbnQgZ2VuZXMgdGhhdCBhcmUgaW4gdGhlIGZpcnN0IGNvbXBhcmlzb24gYnV0IG5vdCBpbiB0aGUgc2Vjb25kIQpXZSBjYW4gdXNlIGBzZXRkaWZmKClgLCBhIGJhc2UgUiBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIHRvIGdldCB0aGUgbGlzdCB0aGF0IHdlIHdhbnQuCgpgYGB7ciBzZXRkaWZmfQojIFdoYXQgZ2VuZXMgYXJlIGluIHRoZSBmaXJzdCBzZXQgYnV0ICpub3QqIGluIHRoZSBzZWNvbmQgc2V0CmdlbmVzX2Zvcl9vcmEgPC0gc2V0ZGlmZih2c19sb3dfZ2VuZXMsIHZzX3Vuc29ydGVkX2dlbmVzKQpgYGAKCiMjIyMgQmFja2dyb3VuZCBzZXQKCkFzIHdlIHNhdyBhYm92ZSwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUgcmVsaWVzIG9uIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGJhY2tncm91bmQgZGlzdHJpYnV0aW9uLgpTb21ldGltZXMgZm9sa3MgY29uc2lkZXIgZ2VuZXMgZnJvbSB0aGUgZW50aXJlIGdlbm9tZSB0byBjb21wcmlzZSB0aGUgYmFja2dyb3VuZCwgYnV0IGluIHRoZSBleGFtcGxlIGJvcnJvd2VkIGZyb20gdGhlIGBjbHVzdGVyUHJvZmlsZXJgIGF1dGhvcnMsIHRoZXkgc3RhdGU6Cgo+IDE3LDk4MCBnZW5lcyBkZXRlY3RlZCBpbiBhIE1pY3JvYXJyYXkgc3R1ZHkKCldoZXJlIHRoZSBrZXkgcGhyYXNlIGlzICoqZ2VuZXMgZGV0ZWN0ZWQqKi4KCklmIHdlIHdlcmUgdW5hYmxlIHRvIGluY2x1ZGUgYSBnZW5lIGluIG9uZSBvZiBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMgYmVjYXVzZSwgZm9yIGV4YW1wbGUsIGl0IGhhZCBsb3cgbWVhbiBleHByZXNzaW9uIGluIG91ciBleHBlcmltZW50IGFuZCB0aGVyZWZvcmUgd2FzIGZpbHRlcmVkIG91dCBpbiBvdXIgYHRpZHlyOjpkcm9wX25hKClgIHN0ZXAsIHdlIHNob3VsZG4ndCBpbmNsdWRlIGluIG91ciBiYWNrZ3JvdW5kIHNldC4KCldlIGNhbiB1c2UgYW5vdGhlciBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIGBpbnRlcnNlY3QoKWAsIHRvIGdldCBvdXIgYmFja2dyb3VuZCBzZXQgb2YgZ2VuZXMgdGhhdCB3ZXJlIGluY2x1ZGVkIGluIGJvdGggY29tcGFyaXNvbnMuCgpgYGB7ciBnZXRfYmFja2dyb3VuZF9zZXR9CiMgaW50ZXJzZWN0KCkgd2lsbCByZXR1cm4gdGhlIGdlbmVzIGluIGJvdGggc2V0cyAtIHdlIGFyZSB1c2luZyB0aGUgZW50aXJlIGRhdGEKIyBmcmFtZSBoZXJlIChjb21wbGV0ZSBjYXNlcyksIG5vdCBqdXN0IHRoZSBzaWduaWZpY2FudCBnZW5lcwpiYWNrZ3JvdW5kX3NldCA8LSBpbnRlcnNlY3QodnNfbG93X2RmJGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdnNfdW5zb3J0ZWRfZGYkZ2VuZV9zeW1ib2wpCgojIFJlbW92ZSBhbnl0aGluZyB0aGF0IGNvdWxkbid0IHJlbGlhYmx5IGJlIG1lYXN1cmVkL2Fzc2Vzc2VkIGluIGJvdGggZnJvbSB0aGUKIyBnZW5lcyBvZiBpbnRlcmVzdCBsaXN0IC0gdXNpbmcgaW50ZXJzZWN0KCkgd2lsbCBkcm9wIGFueXRoaW5nIGluIHRoZSBmaXJzdCBzZXQKIyB0aGF0IGlzbid0IGFsc28gaW4gdGhlIHNlY29uZCBzZXQKZ2VuZXNfZm9yX29yYSA8LSBpbnRlcnNlY3QoZ2VuZXNfZm9yX29yYSwgYmFja2dyb3VuZF9zZXQpCmBgYAoKIyMjIyBSdW4gYGVucmljaGVyKClgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBiYWNrZ3JvdW5kIHNldCwgb3VyIGdlbmVzIG9mIGludGVyZXN0LCBhbmQgb3VyIHBhdGh3YXkgaW5mb3JtYXRpb24sIHdlJ3JlIHJlYWR5IHRvIHJ1biBPUkEgdXNpbmcgdGhlIGBlbnJpY2hlcigpYCBmdW5jdGlvbi4KCmBgYHtyIGtlZ2dfb3JhfQprZWdnX29yYV9yZXN1bHRzIDwtIGVucmljaGVyKAogIGdlbmUgPSBnZW5lc19mb3Jfb3JhLCAgIyBHZW5lcyBvZiBpbnRlcmVzdAogIHB2YWx1ZUN1dG9mZiA9IDAuMDUsCiAgcEFkanVzdE1ldGhvZCA9ICJCSCIsICAjIEZEUgogIHVuaXZlcnNlID0gYmFja2dyb3VuZF9zZXQsICAjIEJhY2tncm91bmQgc2V0CiAgIyBUaGUgcGF0aHdheSBpbmZvcm1hdGlvbiBzaG91bGQgYmUgYSBkYXRhIGZyYW1lIHdpdGggYSB0ZXJtIG5hbWUgb3IKICAjIGlkZW50aWZpZXIgYW5kIHRoZSBnZW5lIGlkZW50aWZpZXJzCiAgVEVSTTJHRU5FID0gZHBseXI6OnNlbGVjdChtbV9rZWdnX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3NfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc3ltYm9sKQopCmBgYAoKKk5vdGU6IHVzaW5nIGBlbnJpY2hLRUdHKClgIGlzIGEgc2hvcnRjdXQgZm9yIGRvaW5nIE9SQSB1c2luZyBLRUdHLCBidXQgdGhlIGFwcHJvYWNoIHdlIGNvdmVyZWQgaGVyZSBjYW4gYmUgdXNlZCB3aXRoIGFueSBnZW5lIHNldHMgeW91J2QgbGlrZSEqCgpXaGF0IGlzIHJldHVybmVkIGJ5IGBlbnJpY2hlcigpYD8KCmBgYHtyIHZpZXdfa2VnZ19vcmEsIGV2YWwgPSBGQUxTRX0KVmlldyhrZWdnX29yYV9yZXN1bHRzKQpgYGAKClRoZSBpbmZvcm1hdGlvbiB3ZSdyZSBtb3N0IGxpa2VseSBpbnRlcmVzdGVkIGluIGlzIGluIHRoZSBgcmVzdWx0c2Agc2xvdC4KTGV0J3MgY29udmVydCB0aGlzIGludG8gYSBkYXRhIGZyYW1lIHRoYXQgd2UgY2FuIHdyaXRlIHRvIGZpbGUuCgpgYGB7ciBrZWdnX2RmfQprZWdnX3Jlc3VsdF9kZiA8LSBkYXRhLmZyYW1lKGtlZ2dfb3JhX3Jlc3VsdHNAcmVzdWx0KQpgYGAKCiMjIyMgVmlzdWFsaXppbmcgcmVzdWx0cwoKV2UgY2FuIHVzZSBhIGRvdCBwbG90IHRvIHZpc3VhbGl6ZSBvdXIgc2lnbmlmaWNhbnQgZW5yaWNobWVudCByZXN1bHRzLgoKYGBge3IgZG90cGxvdCwgbGl2ZSA9IFRSVUV9CmVucmljaHBsb3Q6OmRvdHBsb3Qoa2VnZ19vcmFfcmVzdWx0cykKYGBgCgpXZSBjYW4gdXNlIGFuIFtVcFNldCBwbG90XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM0NzIwOTkzLykgdG8gdmlzdWFsaXplIHRoZSAqKm92ZXJsYXAqKiBiZXR3ZWVuIHRoZSBnZW5lIHNldHMgdGhhdCB3ZXJlIHJldHVybmVkIGFzIHNpZ25pZmljYW50LgoKYGBge3IgdXBzZXRwbG90LCBsaXZlID0gVFJVRX0KZW5yaWNocGxvdDo6dXBzZXRwbG90KGtlZ2dfb3JhX3Jlc3VsdHMpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHNvbWUgb2YgdGhlIEROQSByZXBhaXIgcGF0aHdheXMgc2hhcmUgZ2VuZXMuCkdlbmUgc2V0cyBvciBwYXRod2F5cyBhcmVuJ3QgaW5kZXBlbmRlbnQsIGVpdGhlciEKU29tZXRpbWVzIG11bHRpcGxlIHBhdGh3YXlzIHRoYXQgc2hvdyB1cCBpbiBvdXIgcmVzdWx0cyBhcyBzaWduaWZpY2FudCBhcmUgaW5kaWNhdGl2ZSBvZiBvbmx5IGEgaGFuZGZ1bCBvZiBnZW5lcyBpbiBvdXIgZ2VuZSBsaXN0LgoKV2UgY2FuIGxvb2sgYXQgdGhlIGBnZW5lSURgIGNvbHVtbiBvZiBvdXIgcmVzdWx0cyB0byBzZWUgd2hhdCBnZW5lcyBvdmVybGFwOyBpdCdzIGEgZ29vZCBpZGVhIHRvIHRha2UgYSBsb29rLgoKYGBge3IgbG9va19hdF9nZW5lX2lkcywgbGl2ZSA9IFRSVUUsIHJvd25hbWVzLnByaW50ID0gRkFMU0V9CmtlZ2dfcmVzdWx0X2RmIHw+CiAgIyBVc2UgZHBseXI6OnNlbGVjdCgpIC0gdGhlIG5hbWUgb2YgdGhlIHBhdGh3YXkgaXMgaW4gdGhlIElEIGNvbHVtbgogIGRwbHlyOjpzZWxlY3QoSUQsIGdlbmVJRCkKYGBgCgojIyMjIFdyaXRlIHJlc3VsdHMgdG8gZmlsZQoKYGBge3Igd3JpdGVfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CnJlYWRyOjp3cml0ZV90c3Yoa2VnZ19yZXN1bHRfZGYsIGZpbGUgPSBrZWdnX3Jlc3VsdHNfZmlsZSkKYGBgCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb25faW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
    @@ -1127,7 +3791,7 @@

    Session Info

    $(document).ready(function () { $('.tabset-dropdown > .nav-tabs > li').click(function () { - $(this).parent().toggleClass('nav-tabs-open') + $(this).parent().toggleClass('nav-tabs-open'); }); }); @@ -1143,6 +3807,9 @@

    Session Info

    -

    Since this data frame of DGE results includes gene symbols, we do not @@ -3118,7 +3124,7 @@

    Differential gene expression results

    will count the number of duplicate values.

    - +
    sum(duplicated(dge_results_df$gene_symbol))
    @@ -3141,7 +3147,7 @@

    Removing duplicates

    explore our filtering steps.

    - +
    duplicated_gene_symbols <- dge_results_df |>
       dplyr::filter(duplicated(gene_symbol)) |>
       dplyr::pull(gene_symbol)
    @@ -3151,18 +3157,16 @@

    Removing duplicates

    Now we’ll look at the values for the the duplicated gene symbols.

    - +
    dge_results_df |>
       dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
       dplyr::arrange(gene_symbol)
    -
    -

    We can see that the associated values vary for each row.

    @@ -3185,7 +3189,7 @@

    Removing duplicates

    value.

    - +
    filtered_dge_df <- dge_results_df |>
       # Sort so that the highest absolute values of the log2 fold change are at the
       # top
    @@ -3198,20 +3202,18 @@ 

    Removing duplicates

    Let’s see what happened to our duplicate identifiers.

    - +
    # Subset to & arrange by gene symbols that were duplicated in the original
     # data frame of results
     filtered_dge_df |>
       dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
       dplyr::arrange(gene_symbol)
    -
    -

    Now we’re ready to prep our pre-ranked list for GSEA.

    @@ -3223,7 +3225,7 @@

    Pre-ranked list

    identifiers. This is step 1 – gene-level statistics.

    - +
    lfc_vector <- filtered_dge_df |>
       # Extract a vector of `log2FoldChange` named by `gene_symbol`
       dplyr::pull(log2FoldChange, name = gene_symbol)
    @@ -3234,7 +3236,7 @@ 

    Pre-ranked list

    Let’s look at the top ranked values.

    - +
    # Look at first entries of the log2 fold change vector
     head(lfc_vector)
    @@ -3247,7 +3249,7 @@

    Pre-ranked list

    And the bottom of the list.

    - +
    # Look at the last entries of the log2 fold change vector
     tail(lfc_vector)
    @@ -3267,7 +3269,7 @@

    Run GSEA

    specific, commonly used gene sets (e.g., gseKEGG()).

    - +
    gsea_results <- GSEA(geneList = lfc_vector,  # ordered ranked gene list
                          minGSSize = 25,  # minimum gene set size
                          maxGSSize = 500,  # maximum gene set set
    @@ -3277,15 +3279,25 @@ 

    Run GSEA

    gs_name, gene_symbol))
    - -
    using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).
    -
    -preparing geneSet collections...
    -GSEA analysis...
    -Warning: There are ties in the preranked stats (0.1% of the list).
    -The order of those tied genes will be arbitrary, which may produce unexpected results.leading edge analysis...
    -done...
    - + +
    using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).
    + + +
    preparing geneSet collections...
    + + +
    GSEA analysis...
    + + +
    Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (0.1% of the list).
    +The order of those tied genes will be arbitrary, which may produce unexpected results.
    + + +
    leading edge analysis...
    + + +
    done...
    +

    The warning about ties means that there are multiple genes that have @@ -3309,7 +3321,7 @@

    Run GSEA

    Let’s write these results to file.

    - +
    gsea_results@result |> readr::write_tsv(gsea_results_file)
    @@ -3328,14 +3340,14 @@

    Highly Positive NES

    higher expression value in MYCN amplified cell lines.

    - +
    enrichplot::gseaplot(gsea_results,
                          geneSetID = "HALLMARK_MYC_TARGETS_V1",
                          title = "HALLMARK_MYC_TARGETS_V1",
                          color.line = "#0066FF")
    - -

    + +

    @@ -3349,14 +3361,14 @@

    Highly Negative NES

    highly negative NES.

    - +
    enrichplot::gseaplot(gsea_results,
                          geneSetID = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
                          title = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
                          color.line = "#0066FF")
    - -

    + +

    @@ -3372,14 +3384,14 @@

    A non-significant result

    viewed earlier.

    - +
    enrichplot::gseaplot(gsea_results,
                          geneSetID = "HALLMARK_P53_PATHWAY",
                          title = "HALLMARK_P53_PATHWAY",
                          color.line = "#0066FF")
    - -

    + +

    @@ -3395,11 +3407,11 @@

    A non-significant result

    Session Info

    - +
    sessionInfo()
    - -
    R version 4.4.0 (2024-04-24)
    +
    +
    R version 4.4.1 (2024-06-14)
     Platform: x86_64-pc-linux-gnu
     Running under: Ubuntu 22.04.4 LTS
     
    @@ -3408,9 +3420,12 @@ 

    Session Info

    LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0 locale: - [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8 LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 - [6] LC_MESSAGES=C.UTF-8 LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C -[11] LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C + [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C + [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 + [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 + [7] LC_PAPER=en_US.UTF-8 LC_NAME=C + [9] LC_ADDRESS=C LC_TELEPHONE=C +[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C time zone: Etc/UTC tzcode source: system (glibc) @@ -3419,32 +3434,44 @@

    Session Info

    [1] stats graphics grDevices utils datasets methods base other attached packages: -[1] msigdbr_7.5.1 clusterProfiler_4.12.0 +[1] msigdbr_7.5.1 clusterProfiler_4.12.0 optparse_1.7.5 loaded via a namespace (and not attached): - [1] DBI_1.2.2 shadowtext_0.1.3 gson_0.1.0 gridExtra_2.3 rlang_1.1.3 - [6] magrittr_2.0.3 DOSE_3.30.0 compiler_4.4.0 RSQLite_2.3.6 png_0.1-8 - [11] vctrs_0.6.5 reshape2_1.4.4 stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2 - [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3 ggraph_2.2.1 utf8_1.2.4 - [21] HDO.db_0.99.1 tzdb_0.4.0 enrichplot_1.24.0 UCSC.utils_1.0.0 purrr_1.0.2 - [26] bit_4.0.5 xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8 aplot_0.2.2 - [31] GenomeInfoDb_1.40.0 jsonlite_1.8.8 blob_1.2.4 BiocParallel_1.38.0 tweenr_2.0.3 - [36] parallel_4.4.0 R6_2.5.1 stringi_1.8.3 RColorBrewer_1.1-3 GOSemSim_2.30.0 - [41] knitr_1.46 Rcpp_1.0.12 readr_2.1.5 IRanges_2.38.0 Matrix_1.7-0 - [46] splines_4.4.0 igraph_2.0.3 tidyselect_1.2.1 qvalue_2.36.0 rstudioapi_0.16.0 - [51] viridis_0.6.5 codetools_0.2-20 lattice_0.22-6 tibble_3.2.1 plyr_1.8.9 - [56] Biobase_2.64.0 treeio_1.28.0 withr_3.0.0 KEGGREST_1.44.0 gridGraphics_0.5-1 - [61] scatterpie_0.2.2 polyclip_1.10-6 Biostrings_2.72.0 pillar_1.9.0 ggtree_3.12.0 - [66] stats4_4.4.0 ggfun_0.1.4 generics_0.1.3 vroom_1.6.5 hms_1.1.3 - [71] S4Vectors_0.42.0 ggplot2_3.5.1 munsell_0.5.1 scales_1.3.0 tidytree_0.4.6 - [76] glue_1.7.0 lazyeval_0.2.2 tools_4.4.0 data.table_1.15.4 fgsea_1.30.0 - [81] babelgene_22.9 fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4 tidygraph_1.3.1 - [86] cowplot_1.1.3 grid_4.4.0 tidyr_1.3.1 ape_5.8 AnnotationDbi_1.66.0 - [91] colorspace_2.1-0 nlme_3.1-164 GenomeInfoDbData_1.2.12 patchwork_1.2.0 ggforce_0.4.2 - [96] cli_3.6.2 fansi_1.0.6 viridisLite_0.4.2 dplyr_1.1.4 gtable_0.3.5 -[101] yulab.utils_0.1.4 digest_0.6.35 BiocGenerics_0.50.0 ggrepel_0.9.5 ggplotify_0.1.2 -[106] farver_2.1.1 memoise_2.0.1 lifecycle_1.0.4 httr_1.4.7 GO.db_3.19.1 -[111] bit64_4.0.5 MASS_7.3-60.2
    + [1] DBI_1.2.2 gson_0.1.0 shadowtext_0.1.3 + [4] gridExtra_2.3 rlang_1.1.3 magrittr_2.0.3 + [7] DOSE_3.30.0 compiler_4.4.1 RSQLite_2.3.6 + [10] png_0.1-8 vctrs_0.6.5 reshape2_1.4.4 + [13] stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2 + [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3 + [19] ggraph_2.2.1 utf8_1.2.4 HDO.db_0.99.1 + [22] rmarkdown_2.26 tzdb_0.4.0 enrichplot_1.24.0 + [25] UCSC.utils_1.0.0 purrr_1.0.2 bit_4.0.5 + [28] xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8 + [31] aplot_0.2.2 GenomeInfoDb_1.40.0 jsonlite_1.8.8 + [34] blob_1.2.4 highr_0.10 BiocParallel_1.38.0 + [37] tweenr_2.0.3 parallel_4.4.1 R6_2.5.1 + [40] bslib_0.7.0 stringi_1.8.3 RColorBrewer_1.1-3 + [43] jquerylib_0.1.4 GOSemSim_2.30.0 Rcpp_1.0.12 + [46] knitr_1.46 readr_2.1.5 IRanges_2.38.0 + [49] Matrix_1.7-0 splines_4.4.1 igraph_2.0.3 + [52] tidyselect_1.2.1 qvalue_2.36.0 yaml_2.3.8 + [55] viridis_0.6.5 codetools_0.2-20 lattice_0.22-6 + [58] tibble_3.2.1 plyr_1.8.9 treeio_1.28.0 + [61] Biobase_2.64.0 withr_3.0.0 KEGGREST_1.44.0 + [64] evaluate_0.23 gridGraphics_0.5-1 scatterpie_0.2.2 + [67] getopt_1.20.4 polyclip_1.10-6 Biostrings_2.72.0 + [70] ggtree_3.12.0 pillar_1.9.0 stats4_4.4.1 + [73] ggfun_0.1.4 generics_0.1.3 vroom_1.6.5 + [76] hms_1.1.3 S4Vectors_0.42.0 ggplot2_3.5.1 + [79] tidytree_0.4.6 munsell_0.5.1 scales_1.3.0 + [82] glue_1.7.0 lazyeval_0.2.2 tools_4.4.1 + [85] data.table_1.15.4 fgsea_1.30.0 babelgene_22.9 + [88] fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4 + [91] tidygraph_1.3.1 cowplot_1.1.3 grid_4.4.1 + [94] ape_5.8 tidyr_1.3.1 AnnotationDbi_1.66.0 + [97] colorspace_2.1-0 nlme_3.1-164 patchwork_1.2.0 +[100] GenomeInfoDbData_1.2.12 + [ reached getOption("max.print") -- omitted 20 entries ]
    diff --git a/pathway-analysis/03-gene_set_variation_analysis-live.Rmd b/pathway-analysis/03-gene_set_variation_analysis-live.Rmd index 13369210..84aa07d3 100644 --- a/pathway-analysis/03-gene_set_variation_analysis-live.Rmd +++ b/pathway-analysis/03-gene_set_variation_analysis-live.Rmd @@ -1,11 +1,11 @@ --- title: "Pathway analysis: Gene Set Variation Analysis (GSVA)" -output: +output: html_notebook: toc: true toc_float: true author: CCDL for ALSF -date: 2020 +date: 2024 --- ## Objectives @@ -42,8 +42,6 @@ Note that these scores will depend on the samples included in the dataset when y ### Libraries ```{r libraries} -# Pipes -library(magrittr) # Gene Set Variation Analysis library(GSVA) ``` @@ -53,12 +51,12 @@ library(GSVA) #### Directories ```{r directories} -# We have some medulloblastoma data from the OpenPBTA project that we've +# We have some medulloblastoma data from the OpenPBTA project that we've # prepared ahead of time input_dir <- file.path("data", "open-pbta") # Create a directory specifically for the results using this dataset -output_dir <- file.path("results", "open-pbta") +output_dir <- file.path("results", "open-pbta") if (!dir.exists(output_dir)) { dir.create(output_dir, recursive = TRUE) } @@ -139,7 +137,7 @@ For GSVA, we need a matrix. You may notice that GSVA has some commonalities with GSEA. Rather than ranking genes based on some statistic _we_ selected ahead of time, GSVA fits a model and ranks genes based on their expression level relative to the sample distribution. This is a way of asking if a gene _i_ is highly or lowly expressed in a sample _j_ in the context of this experiment and ranking accordingly ([Hänzelmann _et al._ 2013](https://doi.org/10.1186/1471-2105-14-7)). -The pathway-level score calculated is a way of asking how genes _within_ a gene set vary as compared to genes that are _outside_ of that gene set ([Malhotra. 2018](https://towardsdatascience.com/decoding-gene-set-variation-analysis-8193a0cfda3)). +The pathway-level score calculated is a way of asking how genes _within_ a gene set vary as compared to genes that are _outside_ of that gene set ([Malhotra. 2018](https://towardsdatascience.com/decoding-gene-set-variation-analysis-8193a0cfda3)). (This is sometimes called a competitive test in gene set enrichment literature.) The intuition here is that we will get pathway-level scores for each sample that indicate if genes in a pathway vary concordantly in one direction (overexpressed or underexpressed relative to the overall population) ([Hänzelmann _et al._ 2013](https://doi.org/10.1186/1471-2105-14-7)). @@ -147,21 +145,30 @@ The output is a gene set by sample matrix of GSVA scores. ### Perform GSVA +The main work for GSVA is done by the `gsva()` function, which can actually do a variety of different enrichment score calculations. +To specify that we want the algorithm used by [Hänzelmann _et al._ (2013)](https://doi.org/10.1186/1471-2105-14-7), we will pass as the first argument a call to the `gsvaParam()` function, which is where we will put our data, gene sets, and other parameters specific to that algorithm. + ```{r run_gsva} -gsva_results <- gsva(rnaseq_mat, - hallmarks_list, - method = "gsva", - # Appropriate for our transformed data on log2-like scale - kcdf = "Gaussian", - # Minimum gene set size - min.sz = 15, - # Maximum gene set size - max.sz = 500, - # Compute Gaussian-distributed scores - mx.diff = TRUE) +gsva_results <- gsva( + gsvaParam( + exprData = rnaseq_mat, + geneSets = hallmarks_list, + # Minimum gene set size + minSize = 15, + # Maximum gene set size + maxSize = 500, + # Kernel for estimation + # Gaussian for our transformed data on log2-like scale + kcdf = "Gaussian", + # enrichment score is the difference between largest positive and negative + maxDiff = TRUE + ), + # Use 4 cores for multiprocessing + BPPARAM = BiocParallel::MulticoreParam(4) +) ``` -**Note: the `gsva()` documentation says we can use `kcdf = "Gaussian"` if we had RNA-seq log-CPMs, log-RPKMs or log-TPMs, but we would use `kcdf = "Poisson"` on integer counts.** +**Note: the `gsvaParam()` documentation says we can use `kcdf = "Gaussian"` if we had RNA-seq log-CPMs, log-RPKMs or log-TPMs, but we would use `kcdf = "Poisson"` on integer counts.** ```{r gsva_peek} # Let's explore what the output of gsva() looks like @@ -169,7 +176,7 @@ gsva_results[1:5, 1:5] ``` ### A note on gene set size - + Often the scores of gene set enrichment methods are not comparable between gene sets of different sizes. Let's do an experiment using randomly generated gene sets to explore this idea a bit more. @@ -184,7 +191,7 @@ all_genes <- rownames(rnaseq_mat) set.seed(2020) ``` -Our minimum gene set size earlier was 15 genes and our maximum gene set size was 500 genes. +Our minimum gene set size earlier was 15 genes and our maximum gene set size was 500 genes. We'll use the same minimum and maximum values for our random gene sets and some values in between. ```{r gene_set_sizes} @@ -198,7 +205,7 @@ For each gene set size, we will generate 100 random gene sets. # Set number of replicates nreps <- 100 # Generate 100 random gene sets of each size -random_gene_sets <- rep(gene_set_size, nreps) %>% # Repeat gene sizes so we run `nreps` times +random_gene_sets <- rep(gene_set_size, nreps) |> # Repeat gene sizes so we run `nreps` times purrr::map( # Sample the vector of all genes, choosing the number of items specified # in the element of gene set size @@ -210,35 +217,28 @@ random_gene_sets <- rep(gene_set_size, nreps) %>% # Repeat gene sizes so we run The Hallmarks list we used earlier stored the gene set names as the name of the list, so let's add names to our random gene sets that indicate what size they are and so `gsva()` doesn't get upset. ```{r name_random_gene_sets} -# We will include the size of the gene set in the gene set name +# We will include the size of the gene set in the gene set name # Start by taking the length of each pathway and appending "pathway_" to that # number -lengths_vector <- random_gene_sets %>% +lengths_vector <- random_gene_sets |> # Get the length of each gene set (number of genes) - purrr::map(~ length(.x)) %>% + purrr::map(~ length(.x)) |> # Make it "pathway_" - purrr::map(~ paste0("pathway_", .x)) %>% + purrr::map(~ paste0("pathway_", .x)) |> # Return a vector purrr::flatten_chr() # Add the names in lengths_vector to the list - "pathway_" -random_gene_sets <- random_gene_sets %>% +random_gene_sets <- random_gene_sets |> # make.names() appends a "version" if something is not unique purrr::set_names(nm = make.names(lengths_vector, unique = TRUE)) ``` - + Run GSVA on our dataset with the same parameters as before, but now with random gene sets. ```{r random_gsva, live = TRUE} - # Appropriate for our transformed data on - # log2-like scale - - # Minimum gene set size - - # Maximum gene set size - - # Compute Gaussian-distributed scores + # Use 4 cores for multiprocessing ``` @@ -247,20 +247,22 @@ First we need to get this data in an appropriate format for `ggplot2`. ```{r longer_random_gsva} # The random results are a matrix -random_long_df <- random_gsva_results %>% - data.frame() %>% +random_long_df <- random_gsva_results |> + data.frame() |> # Gene set names are rownames - tibble::rownames_to_column("gene_set") %>% + tibble::rownames_to_column("gene_set") |> # Get into long format - tidyr::pivot_longer(cols = -gene_set, - names_to = "Kids_First_Biospecimen_ID", - values_to = "gsva_score") %>% + tidyr::pivot_longer( + cols = -gene_set, + names_to = "Kids_First_Biospecimen_ID", + values_to = "gsva_score" + ) |> # Remove the .version added by make.names() - dplyr::mutate(gene_set = stringr::str_remove(gene_set, "\\..*")) %>% + dplyr::mutate(gene_set = stringr::str_remove(gene_set, "\\..*")) |> # Add a column that keeps track of the gene set size - dplyr::mutate(gene_set_size = stringr::word(gene_set, 2, sep = "_")) %>% + dplyr::mutate(gene_set_size = stringr::word(gene_set, 2, sep = "_")) |> # We want to plot smallest no. genes -> largest no. genes - dplyr::mutate(gene_set_size = factor(gene_set_size, + dplyr::mutate(gene_set_size = factor(gene_set_size, levels = c(15, 25, 50, 100, 250, 500))) ``` @@ -269,8 +271,8 @@ Let's make a violin plot so we can look at the distribution of scores by gene se ```{r random_violin} # Violin plot comparing GSVA scores of different random gene set sizes -random_long_df %>% - ggplot2::ggplot(ggplot2::aes(x = gene_set_size, +random_long_df |> + ggplot2::ggplot(ggplot2::aes(x = gene_set_size, y = gsva_score)) + # Make a violin plot that's a pretty blue! ggplot2::geom_violin(fill = "#99CCFF", alpha = 0.5) + @@ -297,7 +299,7 @@ How might you use this information to inform your interpretation of GSVA scores? ### How can you use these scores? If you did have groups information for your samples, you could test for pathway scores differences between groups. -Here's [an example](https://htmlpreview.github.io/?https://github.com/AlexsLemonade/OpenPBTA-analysis/blob/9b44bf1c186b3126b16dbe5b87756b3eae3feec2/analyses/gene-set-enrichment-analysis/02-model-gsea.nb.html) of that from the OpenPBTA project itself! +Here's [an example](https://htmlpreview.github.io/?https://github.com/AlexsLemonade/OpenPBTA-analysis/blob/9b44bf1c186b3126b16dbe5b87756b3eae3feec2/analyses/gene-set-enrichment-analysis/02-model-gsea.nb.html) of that from the OpenPBTA project itself! You can also visualize this matrix in a heatmap. Here's a figure from the OpenPBTA project, where the middle panel is a heatmap of GSVA scores that were significantly different between histologies. @@ -309,9 +311,9 @@ Here's a figure from the OpenPBTA project, where the middle panel is a heatmap o ### Write results to file ```{r write_gsva_results} -gsva_results %>% - as.data.frame() %>% - tibble::rownames_to_column("pathway") %>% +gsva_results |> + as.data.frame() |> + tibble::rownames_to_column("pathway") |> readr::write_tsv(file = gsva_results_file) ``` diff --git a/pathway-analysis/03-gene_set_variation_analysis.nb.html b/pathway-analysis/03-gene_set_variation_analysis.nb.html index c3733022..eefefc0f 100644 --- a/pathway-analysis/03-gene_set_variation_analysis.nb.html +++ b/pathway-analysis/03-gene_set_variation_analysis.nb.html @@ -11,43 +11,2661 @@ - + Pathway analysis: Gene Set Variation Analysis (GSVA) - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + +code{white-space: pre-wrap;} +span.smallcaps{font-variant: small-caps;} +span.underline{text-decoration: underline;} +div.column{display: inline-block; vertical-align: top; width: 50%;} +div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} +ul.task-list{list-style: none;} + - @@ -1260,6 +3521,9 @@

    Session Info