Download data here. For how to construct the data objects, see Tutorial.
Quick facts about the datasets:
- Reference data
- Query data
Load library and data.
library(FRmatch)
load("sce-mouse-M1-10X-snRNA-subclass.rda")
load("sce-mouse-M1-10X-scRNA-subclass.rda")
Rename the data objects for simplification.
## query data
sce.query <- sce.mouse.M1.10X.scRNA.subclass
## reference data
sce.ref <- sce.mouse.M1.10X.snRNA.subclass
Take a look at the gene expression data distribution. We preprocessed the 10X data by taking the log1p()
transformation of the raw count data, which was stored in the @assays$logcounts
slot of the data object.
par(mfrow=c(1,2))
hist(logcounts(sce.query))
hist(logcounts(sce.ref))
Since both datasets follow similar distribution, just take the scaling option in the normalization step.
sce.query.scale <- normalization(sce.query, scale=TRUE)
sce.ref.scale <- normalization(sce.ref, scale=TRUE)
Check again the scaled data distribution, ranging from 0 to 1.
par(mfrow=c(1,2))
hist(logcounts(sce.query.scale))
hist(logcounts(sce.ref.scale))
Take an overview of the cell type clusters in both datasets. In FR-Match, we are going to ignore the clusters with very few cells.
## cluster size
plot_clusterSize(sce.query, sce.ref, name.E1 = "Mouse M1 10X scRNA", name.E2 = "Mouse M1 10X snRNA")
The following codes are suggested to plot all cell type barcode plots in an assigned folder. NOT RUN HERE.
ref.clusters <- unique(colData(sce.ref)$cluster_membership)
for(cl in ref.clusters){
plot_cluster_by_markers(sce.ref.scale, cluster.name = cl, name.E1 = "M1_10X_snRNA_",
filename = paste0("plot_barcodes_M1_10X_snRNA/scale_",cl,".pdf"))
}
query.clusters <- unique(colData(sce.query)$cluster_membership)
for(cl in query.clusters){
plot_cluster_by_markers(sce.ref.scale, sce.query.scale, cluster.name = cl, name.E2 = "M1_10X_scRNA_",
filename = paste0("plot_barcodes_M1_10X_scRNA/scale_",cl,".pdf"))
}
Run FR-Match
Cell-to-cluster matching
Below we provide some guidance on parameter configuration.
- We ignore small clusters that have less than 5 cells (
filter.size=5
)
- For cell2cluster matching, it is recommended to use
subsamp.size=10
. Your best choice depends on your data. The choice of this parameter will impact the matching score (i.e. p-value), which is used to determine a match or unassigned match.
use.cosine=TRUE
indicates to use the cosine distance instead of the Euclidean distance in the calculation, which will bring more robustness in matching between different types of samples.
prefix=c("scRNAseq.","snRNAseq.")
is the customizable names of your query and reference data.
rst.cell2cluster <- FRmatch_cell2cluster(sce.query.scale, sce.ref.scale,
filter.size=5, subsamp.size=10, use.cosine=TRUE,
prefix=c("scRNAseq.","snRNAseq."))
The easiest way to look at the results is to use its plot function, which shows the proportion of query cells that are matched to the reference cluster. Note that the column sum is 1.
plot_FRmatch_cell2cluster(rst.cell2cluster, type="match.prop")
The under-partitioned SMC_Peri cluster
In this example, there is an under-partitioned query cluster (SMC_Peri), which is matched in part to two reference clusters (SMC and Peri). We can look at the specific barcodes of the under-partitioned query cluster and the matched reference clusters for more insights.
plot_cluster_by_markers(sce.ref.scale, sce.query.scale, cluster.name = "SMC_Peri", name.E2 = "scRNA.")
plot_cluster_by_markers(sce.ref.scale, cluster.name = "SMC", name.E1 = "snRNA.")
plot_cluster_by_markers(sce.ref.scale, cluster.name = "Peri", name.E1 = "snRNA.")
Available cell2cluster results
Matching results of each query cell can be accessed below.
rst.cell2cluster$cell2cluster
Cluster-to-cluster matching
Though it is suggested to use slightly larger subsampling size (subsamp.size=20
by default) for the cluster-to-cluster matching. Here, we use subsamp.size=10
again in this use case. Your best setting may depend on your data, therefore we recommend to check the cluster sizes in the beginning.
rst <- FRmatch(sce.query.scale, sce.ref.scale,
filter.size=5, subsamp.size=10, use.cosine=TRUE,
prefix=c("scRNAseq.","snRNAseq."))
Visualize the results. Since larger subsampling size is used and the nature of taking a cluster as a whole, the cluster-level matching is more conserved, resulting to some unassigned matches including the under-partitioned cluster.
plot_FRmatch(rst, sig.level=.1)
Other presentations of the results
We may use the following plot to look at the adjusted p-values, and choose a reasonable sig.level
(red dashed line) to determine matches (above the red line) and unassigned matches (below the red line).
plot_FRmatch(rst, type="padj", sig.level = .1)
We can also use the prediction function to list the most similar reference cluster (i.e. highest adjusted p-value) to each query cluster, regardless the sig.level
threshold.
## top match
predict_most_similar_cluster(rst)
Minimum spanning tree plots
Lastly, we can also use the minimum spanning tree (MST) plot to diagnose the under-partitioned cluster and its partial matches.
plot_MST(sce.query.scale, sce.ref.scale, "SMC_Peri", "SMC", use.cosine=T)
runs runs.samp1 runs.samp2 stat p.value
15.000000000 8.000000000 7.000000000 -2.540459701 0.005535343
plot_MST(sce.query.scale, sce.ref.scale, "SMC_Peri", "Peri", use.cosine=T)
runs runs.samp1 runs.samp2 stat p.value
17.0000000000 11.0000000000 6.0000000000 -3.3707719915 0.0003747893
LS0tCnRpdGxlOiAiTWF0Y2hpbmcgbW91c2UgTTEgY2VsbCBzdWJjbGFzc2VzIGFjcm9zcyBkaWZmZXJlbnQgc2FtcGxlIHR5cGVzIC0gc2NSTkFzZXEgYW5kIHNuUk5Bc2VxIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IFRSVUUpCnNldHdkKCJ+L0RvY3VtZW50cy9CSUNDTi1CcmFpblN0YW5kYXJkcy0yMDIwL0FubGF5c2VzL01hcHBpbmcvTW91c2UtTTEvRlJtYXRjaC90dXRvcmlhbC8iKQpgYGAKCi0tLQoKRG93bmxvYWQgZGF0YSBbaGVyZV0oaHR0cHM6Ly9qY3Zpb2ZmaWNlMzY1LW15LnNoYXJlcG9pbnQuY29tLzpmOi9yL3BlcnNvbmFsL3poYW5neV9qY3ZpX29yZy9Eb2N1bWVudHMvRlItTWF0Y2glMjBkZW1vJTIwZGF0YS9UdXRvcmlhbF9tb3VzZV9NMV8xMFhfc2NSTkFzZXFfc25STkFzZXE/Y3NmPTEmd2ViPTEmZT1iUG5hMXIpLiAKRm9yIGhvdyB0byBjb25zdHJ1Y3QgdGhlIGRhdGEgb2JqZWN0cywgc2VlIFtUdXRvcmlhbF0oaHR0cHM6Ly9qY3ZlbnRlcmluc3RpdHV0ZS5naXRodWIuaW8vY2VsbGlncmF0ZS9GUm1hdGNoLXZpZ25ldHRlLmh0bWwpLgoKUXVpY2sgZmFjdHMgYWJvdXQgdGhlIGRhdGFzZXRzOgoKKyBSZWZlcmVuY2UgZGF0YQogICsgQW5hdG9taWMgcmVnaW9uOiBoZWFsdGh5IG1vdXNlIHByaW1hcnkgbW90b3IgY29ydGV4IChNMSkKICArIEV4cGVyaW1lbnRhbCBwbGF0Zm9ybTogMTBYIHYzCiAgKyBTYW1wbGUgdHlwZTogc2luZ2xlLW51Y2xldXMKICArIENpdGF0aW9uOiBbQW4gaW50ZWdyYXRlZCB0cmFuc2NyaXB0b21pYyBhbmQgZXBpZ2Vub21pYyBhdGxhcyBvZiBtb3VzZSBwcmltYXJ5IG1vdG9yIGNvcnRleCBjZWxsIHR5cGVzXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvMjAyMC4wMi4yOS45NzA1NTh2MikuIERhdGEgdXNlZCBoZXJlIGlzIHBhcnQgb2YgdGhlIGludGVncmF0ZWQgdHJhbnNjcmlwdG9taWMgYW5kIGVwaWdlbm9taWMgYXRsYXMgaW4gYWJvdmUgcHVibGljYXRpb24uIEFsc28gdXNlZCBhcyB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgaW4gdGhlIFtBemltdXRoIGFwcF0oaHR0cHM6Ly9hcHAuYXppbXV0aC5odWJtYXBjb25zb3J0aXVtLm9yZy9hcHAvbW91c2UtbW90b3Jjb3J0ZXgpLgorIFF1ZXJ5IGRhdGEKICArIEFuYXRvbWljIHJlZ2lvbjogaGVhbHRoeSBtb3VzZSBwcmltYXJ5IG1vdG9yIGNvcnRleCAoTU9wLCBhLmsuYS4gTTEpCiAgKyBFeHBlcmltZW50YWwgcGxhdGZvcm06IDEwWCB2MgogICsgU2FtcGxlIHR5cGU6IHNpbmdsZS1jZWxsCiAgKyBDaXRhdGlvbjogW0EgdGF4b25vbXkgb2YgdHJhbnNjcmlwdG9taWMgY2VsbCB0eXBlcyBhY3Jvc3MgdGhlIGlzb2NvcnRleCBhbmQgaGlwcG9jYW1wYWwgZm9ybWF0aW9uXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbGwuMjAyMS4wNC4wMjEpLiBEYXRhIHVzZWQgaGVyZSBpcyBwYXJ0IG9mIHRoZSBtb3VzZSB3aG9sZSBjb3J0ZXggdGF4b25vbXkgaW4gdGhlIGFib3ZlIHB1YmxpY2F0aW9uLiBBbHNvIHVzZWQgYXMgdGhlIGRlbW8gZGF0YXNldCBpbiB0aGUgW0F6aW11dGggYXBwXShodHRwczovL2FwcC5hemltdXRoLmh1Ym1hcGNvbnNvcnRpdW0ub3JnL2FwcC9tb3VzZS1tb3RvcmNvcnRleCkuCgotLS0KCkxvYWQgbGlicmFyeSBhbmQgZGF0YS4KYGBge3J9CmxpYnJhcnkoRlJtYXRjaCkKbG9hZCgic2NlLW1vdXNlLU0xLTEwWC1zblJOQS1zdWJjbGFzcy5yZGEiKQpsb2FkKCJzY2UtbW91c2UtTTEtMTBYLXNjUk5BLXN1YmNsYXNzLnJkYSIpCmBgYAoKClJlbmFtZSB0aGUgZGF0YSBvYmplY3RzIGZvciBzaW1wbGlmaWNhdGlvbi4KYGBge3J9CiMjIHF1ZXJ5IGRhdGEKc2NlLnF1ZXJ5IDwtIHNjZS5tb3VzZS5NMS4xMFguc2NSTkEuc3ViY2xhc3MKIyMgcmVmZXJlbmNlIGRhdGEKc2NlLnJlZiA8LSBzY2UubW91c2UuTTEuMTBYLnNuUk5BLnN1YmNsYXNzCmBgYAoKClRha2UgYSBsb29rIGF0IHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YSBkaXN0cmlidXRpb24uIF9XZSBwcmVwcm9jZXNzZWQgdGhlIDEwWCBkYXRhIGJ5IHRha2luZyB0aGUgYGxvZzFwKClgIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSByYXcgY291bnQgZGF0YSwgd2hpY2ggd2FzIHN0b3JlZCBpbiB0aGUgYEBhc3NheXMkbG9nY291bnRzYCBzbG90IG9mIHRoZSBkYXRhIG9iamVjdC5fIApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDEsMikpCmhpc3QobG9nY291bnRzKHNjZS5xdWVyeSkpCmhpc3QobG9nY291bnRzKHNjZS5yZWYpKQpgYGAKClNpbmNlIGJvdGggZGF0YXNldHMgZm9sbG93IHNpbWlsYXIgZGlzdHJpYnV0aW9uLCBqdXN0IHRha2UgdGhlIHNjYWxpbmcgb3B0aW9uIGluIHRoZSBub3JtYWxpemF0aW9uIHN0ZXAuCmBgYHtyfQpzY2UucXVlcnkuc2NhbGUgPC0gbm9ybWFsaXphdGlvbihzY2UucXVlcnksIHNjYWxlPVRSVUUpCnNjZS5yZWYuc2NhbGUgPC0gbm9ybWFsaXphdGlvbihzY2UucmVmLCBzY2FsZT1UUlVFKQpgYGAKCkNoZWNrIGFnYWluIHRoZSBzY2FsZWQgZGF0YSBkaXN0cmlidXRpb24sIHJhbmdpbmcgZnJvbSAwIHRvIDEuCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMSwyKSkKaGlzdChsb2djb3VudHMoc2NlLnF1ZXJ5LnNjYWxlKSkKaGlzdChsb2djb3VudHMoc2NlLnJlZi5zY2FsZSkpCmBgYAoKClRha2UgYW4gb3ZlcnZpZXcgb2YgdGhlIGNlbGwgdHlwZSBjbHVzdGVycyBpbiBib3RoIGRhdGFzZXRzLiBfSW4gRlItTWF0Y2gsIHdlIGFyZSBnb2luZyB0byBpZ25vcmUgdGhlIGNsdXN0ZXJzIHdpdGggdmVyeSBmZXcgY2VsbHMuXwpgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MTB9CiMjIGNsdXN0ZXIgc2l6ZQpwbG90X2NsdXN0ZXJTaXplKHNjZS5xdWVyeSwgc2NlLnJlZiwgbmFtZS5FMSA9ICJNb3VzZSBNMSAxMFggc2NSTkEiLCBuYW1lLkUyID0gIk1vdXNlIE0xIDEwWCBzblJOQSIpCmBgYAoKClRoZSBmb2xsb3dpbmcgY29kZXMgYXJlIHN1Z2dlc3RlZCB0byBwbG90IGFsbCBjZWxsIHR5cGUgYmFyY29kZSBwbG90cyBpbiBhbiBhc3NpZ25lZCBmb2xkZXIuIE5PVCBSVU4gSEVSRS4KYGBge3IsIGV2YWw9RkFMU0V9CnJlZi5jbHVzdGVycyA8LSB1bmlxdWUoY29sRGF0YShzY2UucmVmKSRjbHVzdGVyX21lbWJlcnNoaXApCmZvcihjbCBpbiByZWYuY2x1c3RlcnMpewogIHBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9IGNsLCBuYW1lLkUxID0gIk0xXzEwWF9zblJOQV8iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKCJwbG90X2JhcmNvZGVzX00xXzEwWF9zblJOQS9zY2FsZV8iLGNsLCIucGRmIikpCn0KCnF1ZXJ5LmNsdXN0ZXJzIDwtIHVuaXF1ZShjb2xEYXRhKHNjZS5xdWVyeSkkY2x1c3Rlcl9tZW1iZXJzaGlwKQpmb3IoY2wgaW4gcXVlcnkuY2x1c3RlcnMpewogIHBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIHNjZS5xdWVyeS5zY2FsZSwgY2x1c3Rlci5uYW1lID0gY2wsIG5hbWUuRTIgPSAiTTFfMTBYX3NjUk5BXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoInBsb3RfYmFyY29kZXNfTTFfMTBYX3NjUk5BL3NjYWxlXyIsY2wsIi5wZGYiKSkKfQpgYGAKCgojIFJ1biBGUi1NYXRjaAoKIyMgQ2VsbC10by1jbHVzdGVyIG1hdGNoaW5nCgpCZWxvdyB3ZSBwcm92aWRlIHNvbWUgZ3VpZGFuY2Ugb24gcGFyYW1ldGVyIGNvbmZpZ3VyYXRpb24uCgogICsgV2UgaWdub3JlIHNtYWxsIGNsdXN0ZXJzIHRoYXQgaGF2ZSBsZXNzIHRoYW4gNSBjZWxscyAoYGZpbHRlci5zaXplPTVgKQogICsgRm9yIGNlbGwyY2x1c3RlciBtYXRjaGluZywgaXQgaXMgcmVjb21tZW5kZWQgdG8gdXNlIGBzdWJzYW1wLnNpemU9MTBgLiBZb3VyIGJlc3QgY2hvaWNlIGRlcGVuZHMgb24geW91ciBkYXRhLiBUaGUgY2hvaWNlIG9mIHRoaXMgcGFyYW1ldGVyIHdpbGwgaW1wYWN0IHRoZSBtYXRjaGluZyBzY29yZSAoaS5lLiBwLXZhbHVlKSwgd2hpY2ggaXMgdXNlZCB0byBkZXRlcm1pbmUgYSBtYXRjaCBvciB1bmFzc2lnbmVkIG1hdGNoLgogICsgYHVzZS5jb3NpbmU9VFJVRWAgaW5kaWNhdGVzIHRvIHVzZSB0aGUgY29zaW5lIGRpc3RhbmNlIGluc3RlYWQgb2YgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBpbiB0aGUgY2FsY3VsYXRpb24sIHdoaWNoIHdpbGwgYnJpbmcgbW9yZSByb2J1c3RuZXNzIGluIG1hdGNoaW5nIGJldHdlZW4gZGlmZmVyZW50IHR5cGVzIG9mIHNhbXBsZXMuCiAgKyBgcHJlZml4PWMoInNjUk5Bc2VxLiIsInNuUk5Bc2VxLiIpYCBpcyB0aGUgY3VzdG9taXphYmxlIG5hbWVzIG9mIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZSBkYXRhLgoKYGBge3IsIGV2YWw9RkFMU0V9CnJzdC5jZWxsMmNsdXN0ZXIgPC0gRlJtYXRjaF9jZWxsMmNsdXN0ZXIoc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIuc2l6ZT01LCBzdWJzYW1wLnNpemU9MTAsIHVzZS5jb3NpbmU9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVmaXg9Yygic2NSTkFzZXEuIiwic25STkFzZXEuIikpCmBgYAoKVGhlIGVhc2llc3Qgd2F5IHRvIGxvb2sgYXQgdGhlIHJlc3VsdHMgaXMgdG8gdXNlIGl0cyBwbG90IGZ1bmN0aW9uLCB3aGljaCBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiBxdWVyeSBjZWxscyB0aGF0IGFyZSBtYXRjaGVkIHRvIHRoZSByZWZlcmVuY2UgY2x1c3Rlci4gTm90ZSB0aGF0IHRoZSBjb2x1bW4gc3VtIGlzIDEuCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OX0KcGxvdF9GUm1hdGNoX2NlbGwyY2x1c3Rlcihyc3QuY2VsbDJjbHVzdGVyLCB0eXBlPSJtYXRjaC5wcm9wIikKYGBgCgojIyBUaGUgdW5kZXItcGFydGl0aW9uZWQgU01DX1BlcmkgY2x1c3RlcgoKSW4gdGhpcyBleGFtcGxlLCB0aGVyZSBpcyBhbiB1bmRlci1wYXJ0aXRpb25lZCBxdWVyeSBjbHVzdGVyIChTTUNfUGVyaSksIHdoaWNoIGlzIG1hdGNoZWQgaW4gcGFydCB0byB0d28gcmVmZXJlbmNlIGNsdXN0ZXJzIChTTUMgYW5kIFBlcmkpLiBXZSBjYW4gbG9vayBhdCB0aGUgc3BlY2lmaWMgYmFyY29kZXMgb2YgdGhlIHVuZGVyLXBhcnRpdGlvbmVkIHF1ZXJ5IGNsdXN0ZXIgYW5kIHRoZSBtYXRjaGVkIHJlZmVyZW5jZSBjbHVzdGVycyBmb3IgbW9yZSBpbnNpZ2h0cy4KYGBge3IsIGZpZy5oZWlnaHQ9N30KcGxvdF9jbHVzdGVyX2J5X21hcmtlcnMoc2NlLnJlZi5zY2FsZSwgc2NlLnF1ZXJ5LnNjYWxlLCBjbHVzdGVyLm5hbWUgPSAiU01DX1BlcmkiLCBuYW1lLkUyID0gInNjUk5BLiIpCnBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9ICJTTUMiLCBuYW1lLkUxID0gInNuUk5BLiIpCnBsb3RfY2x1c3Rlcl9ieV9tYXJrZXJzKHNjZS5yZWYuc2NhbGUsIGNsdXN0ZXIubmFtZSA9ICJQZXJpIiwgbmFtZS5FMSA9ICJzblJOQS4iKQpgYGAKCgojIyBBdmFpbGFibGUgY2VsbDJjbHVzdGVyIHJlc3VsdHMKCk1hdGNoaW5nIHJlc3VsdHMgb2YgZWFjaCBxdWVyeSBjZWxsIGNhbiBiZSBhY2Nlc3NlZCBiZWxvdy4KYGBge3J9CnJzdC5jZWxsMmNsdXN0ZXIkY2VsbDJjbHVzdGVyCmBgYAoKIyMgQ2x1c3Rlci10by1jbHVzdGVyIG1hdGNoaW5nCgpUaG91Z2ggaXQgaXMgc3VnZ2VzdGVkIHRvIHVzZSBzbGlnaHRseSBsYXJnZXIgc3Vic2FtcGxpbmcgc2l6ZSAoYHN1YnNhbXAuc2l6ZT0yMGAgYnkgZGVmYXVsdCkgZm9yIHRoZSBjbHVzdGVyLXRvLWNsdXN0ZXIgbWF0Y2hpbmcuIEhlcmUsIHdlIHVzZSBgc3Vic2FtcC5zaXplPTEwYCBhZ2FpbiBpbiB0aGlzIHVzZSBjYXNlLiBZb3VyIGJlc3Qgc2V0dGluZyBtYXkgZGVwZW5kIG9uIHlvdXIgZGF0YSwgdGhlcmVmb3JlIHdlIHJlY29tbWVuZCB0byBjaGVjayB0aGUgY2x1c3RlciBzaXplcyBpbiB0aGUgYmVnaW5uaW5nLgpgYGB7ciwgZXZhbD1GQUxTRX0KcnN0IDwtIEZSbWF0Y2goc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAKICAgICAgICAgICAgICAgZmlsdGVyLnNpemU9NSwgc3Vic2FtcC5zaXplPTEwLCB1c2UuY29zaW5lPVRSVUUsCiAgICAgICAgICAgICAgIHByZWZpeD1jKCJzY1JOQXNlcS4iLCJzblJOQXNlcS4iKSkKYGBgCgpWaXN1YWxpemUgdGhlIHJlc3VsdHMuIFNpbmNlIGxhcmdlciBzdWJzYW1wbGluZyBzaXplIGlzIHVzZWQgYW5kIHRoZSBuYXR1cmUgb2YgdGFraW5nIGEgY2x1c3RlciBhcyBhIHdob2xlLCB0aGUgY2x1c3Rlci1sZXZlbCBtYXRjaGluZyBpcyBtb3JlIGNvbnNlcnZlZCwgcmVzdWx0aW5nIHRvIHNvbWUgdW5hc3NpZ25lZCBtYXRjaGVzIGluY2x1ZGluZyB0aGUgdW5kZXItcGFydGl0aW9uZWQgY2x1c3Rlci4KYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CnBsb3RfRlJtYXRjaChyc3QsIHNpZy5sZXZlbD0uMSkKYGBgCgojIyBPdGhlciBwcmVzZW50YXRpb25zIG9mIHRoZSByZXN1bHRzCgpXZSBtYXkgdXNlIHRoZSBmb2xsb3dpbmcgcGxvdCB0byBsb29rIGF0IHRoZSBhZGp1c3RlZCBwLXZhbHVlcywgYW5kIGNob29zZSBhIHJlYXNvbmFibGUgYHNpZy5sZXZlbGAgKHJlZCBkYXNoZWQgbGluZSkgdG8gZGV0ZXJtaW5lIG1hdGNoZXMgKGFib3ZlIHRoZSByZWQgbGluZSkgYW5kIHVuYXNzaWduZWQgbWF0Y2hlcyAoYmVsb3cgdGhlIHJlZCBsaW5lKS4KYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTJ9CnBsb3RfRlJtYXRjaChyc3QsIHR5cGU9InBhZGoiLCBzaWcubGV2ZWwgPSAuMSkKYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIHByZWRpY3Rpb24gZnVuY3Rpb24gdG8gbGlzdCB0aGUgbW9zdCBzaW1pbGFyIHJlZmVyZW5jZSBjbHVzdGVyIChpLmUuIGhpZ2hlc3QgYWRqdXN0ZWQgcC12YWx1ZSkgdG8gZWFjaCBxdWVyeSBjbHVzdGVyLCByZWdhcmRsZXNzIHRoZSBgc2lnLmxldmVsYCB0aHJlc2hvbGQuCmBgYHtyfQojIyB0b3AgbWF0Y2gKcHJlZGljdF9tb3N0X3NpbWlsYXJfY2x1c3Rlcihyc3QpCmBgYAoKCiMjIE1pbmltdW0gc3Bhbm5pbmcgdHJlZSBwbG90cwoKTGFzdGx5LCB3ZSBjYW4gYWxzbyB1c2UgdGhlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSAoTVNUKSBwbG90IHRvIGRpYWdub3NlIHRoZSB1bmRlci1wYXJ0aXRpb25lZCBjbHVzdGVyIGFuZCBpdHMgcGFydGlhbCBtYXRjaGVzLiAKCmBgYHtyLCBmaWcud2lkdGg9NSxmaWcuaGVpZ2h0PTV9CnBsb3RfTVNUKHNjZS5xdWVyeS5zY2FsZSwgc2NlLnJlZi5zY2FsZSwgIlNNQ19QZXJpIiwgIlNNQyIsIHVzZS5jb3NpbmU9VCkKcGxvdF9NU1Qoc2NlLnF1ZXJ5LnNjYWxlLCBzY2UucmVmLnNjYWxlLCAiU01DX1BlcmkiLCAiUGVyaSIsIHVzZS5jb3NpbmU9VCkKYGBgCgo=